Show More
@@ -0,0 +1,109 b'' | |||
|
1 | { | |
|
2 | "worksheets": [ | |
|
3 | { | |
|
4 | "cells": [ | |
|
5 | { | |
|
6 | "source": "A demonstration of the ability to clear the output of a cell during execution.", | |
|
7 | "cell_type": "markdown" | |
|
8 | }, | |
|
9 | { | |
|
10 | "cell_type": "code", | |
|
11 | "language": "python", | |
|
12 | "outputs": [], | |
|
13 | "collapsed": true, | |
|
14 | "prompt_number": 8, | |
|
15 | "input": "from IPython.core.display import clear_output, display" | |
|
16 | }, | |
|
17 | { | |
|
18 | "cell_type": "code", | |
|
19 | "language": "python", | |
|
20 | "outputs": [], | |
|
21 | "collapsed": true, | |
|
22 | "prompt_number": 4, | |
|
23 | "input": "import time" | |
|
24 | }, | |
|
25 | { | |
|
26 | "source": "First we show how this works with ``display``:", | |
|
27 | "cell_type": "markdown" | |
|
28 | }, | |
|
29 | { | |
|
30 | "cell_type": "code", | |
|
31 | "language": "python", | |
|
32 | "outputs": [ | |
|
33 | { | |
|
34 | "output_type": "stream", | |
|
35 | "stream": "stdout", | |
|
36 | "text": "\nTime step: 9" | |
|
37 | }, | |
|
38 | { | |
|
39 | "output_type": "stream", | |
|
40 | "stream": "stdout", | |
|
41 | "text": "\n" | |
|
42 | } | |
|
43 | ], | |
|
44 | "collapsed": false, | |
|
45 | "prompt_number": 20, | |
|
46 | "input": "for i in range(10):\n clear_output()\n print \"Time step: %i\" % i\n time.sleep(0.5)\n" | |
|
47 | }, | |
|
48 | { | |
|
49 | "source": "Next, we show that ``clear_output`` can also be used to create a primitive form of animation using\nmatplotlib:", | |
|
50 | "cell_type": "markdown" | |
|
51 | }, | |
|
52 | { | |
|
53 | "cell_type": "code", | |
|
54 | "language": "python", | |
|
55 | "outputs": [ | |
|
56 | { | |
|
57 | "output_type": "display_data", | |
|
58 | "png": "iVBORw0KGgoAAAANSUhEUgAAAXUAAAD8CAYAAACINTRsAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJztfXuUXUWd7nf6/Uo6SecBmGSIvCaKwaANwRC6A4mJAWGJ\nj3noGmRx45pxOUjfrOs1zBCCznh11gjqBa9ex2FFZY3i1fEyOgqoRCCE2CrCXJsEB40J6byTfvfp\n091n3z8q1adOnaraVbVr77PP6frW6pV0n+6z6+zHt7/9/b76VSYIggAeHh4eHlWBmnIPwMPDw8PD\nHType3h4eFQRPKl7eHh4VBE8qXt4eHhUEType3h4eFQRPKl7eHh4VBFCSf3ll1/GNddcg40bN5a8\n9p3vfAeXX3451q9fj0cffTSWAXp4eHh46CMTllO/6aab8Ja3vAXPPfccnnzyyZmf53I5rFy5Er/6\n1a8AACtXrsTLL7+M9vb2eEfs4eHh4SFFqFJ/7LHHcP3115f8vK+vD8uWLUN7ezva29uxYsUK7N27\nN5ZBenh4eHjoIZTUa2pqIBLzw8PDOO+882a+X7p0KUZHR92OzsPDw8PDCHW2f7hw4UL09/fPfH/0\n6FEsW7as5PcymYztJjw8PDxmNWy6uFinXy6++GKcOHECg4ODOHPmDI4cOYKVK1dKB+a/Atx7771l\nH0Navvy+mJ37YmoqwGc+4/eFzpcttEg9k8nMKO6enh48/vjjqK+vx4MPPoh3vetdWL9+Pe6//37M\nmTPHeiAeHh7Vj9Ongb/923KPorqhZb90dXWhq6sLAPDAAw/M/HzDhg3YsGFDPCPz8PCoOgwPA5OT\nwPQ0UFtb7tFUJ/zkowTR3d1d7iGkBn5fFDCb9sXQEPl3fFz8+mzaF3EhNKceeQOZTCR/yMPDo3rw\n9NNAVxdw4gSwaFG5R5Nu2HJnapS6T0N6eFQ/hofJvzKl7hEdqSD1P/wBuOaaco/Cw8MjboTZLx7R\nkQpSHxggXx4eHtUNr9TjRypIPZsFJibKPQoPD4+44ZV6/EgNqWez5R6Fh4dH3PBKPX6kgtTHx71S\n9/CYDagmUv/c54DBwXKPohSpIHVqv/jko4dHdaOa7JeHHgIOHCj3KEqRGlIHyEwzDw+P6sXwMFBX\nVx2kPjqazoBHqkjd++oeHtWNoSFg8eLqIXVvv0hAydz76h4eejh4sNwjsMPwcHWQehB4UlfCk7qH\nhxk6O4Hjx8s9CnMMDwNLllQOqX/ta8C3v13681yONCXz9osE9ACLSH16GpiaSnY8Hh5pRhAQMjl9\nutwjMUel2S//7/8BL75Y+nPa1sQrdQlUSv1LXwLuvTfZ8Xh4pBm5HBE6aVSJYag0+2VyEhgZKf25\nJ/UQqAqlZ88CJ08mO56k8eMfA37Nbg9d0Kx3JZL60FBl2S9TU+Jmg/RnaTwGqSJ1kVLPZqu/g+O/\n/Avw05+WexQelQKqHNNIKCrQ63vevPhI/aGHgN273b2fV+qWmO2kfugQeaT28NBBpZL68DAwZw7Q\n3Bwfqe/dC+zf7+79pqbkpJ7JeFKXYraT+uHDntQ99FGppD40pCb1XA44cybaNsbH3U5iVNkvixd7\nUpeCHmCRp17tpB4EntQ9zEBJ/ezZ8o7DFMPDwNy5clL/7neBu+6Kto1s1i2pq+yXCy5I5401FaSe\nzQINDXKlLtqp1YKzZ4GxMU/qHvqo1EJpmFI/ezb6tR6HUleRulfqEmSzpHgyG+2Xw4fJv57UPXQx\nMkJEUKWRephSHxmJPgExm3U7r0Vlv5x/PrlRpa0RYWpIvb3dk7qHhw5GRoDXva4ySV2l1EdHo18H\nrpW6yn5pbweamtLnJHhSLzMOHQLa2jype+hjZARYtqzySD3MfnGl1JOyX1pbCW+lzYJJFamrCqVp\ne8RxhcOHgYsu8qTuQfD44+HneqWSepj9kkalPjVFxsS/JyX1efM8qQsxPq721IOgcmagmcKTugeL\nm28OfzIdHgaWLq289EslKnX6XvwxGR0FWlqIGE3bzTUVpB5mvwDVa8F4Uq9O/OAHwJ13mv/d5GR4\noY/11CvpCTaJQmkcSh0Qk7q3XxQIS78AYlK/8850LidlAk/q1Ynjx4H+frO/yecJSYeR0sgIsGAB\n0NhYWWKHLZTSJ3AWLuwX1+kXeix4X93bLyEIU+pz5ohP3mefBV57Lf7xxYV8HjhyBHj96z2pVxty\nOXPVSQlEh9Tb2gihpO3RXwVqv9TUAPX1pfsnqlKfnhb731FAbxAyUvf2iwRhhdKODnEFemiostc1\nPX6cfO65cz2pVxtsSJ0SiI79MmdO5ZE6tV8AsQUTVanHsdbx1JRYVI6NeftFiqkpoljb2uRKvaND\nrNSHhyt7AY3Dh4Hly8lEEk/q1YUklPr8+ZVVLKVKHRCTelSlHgepT06Sm6e3XwyQzZIAf1OTOalX\nulI/fJhE0zypVx+ikHqYUBkerkz7JUypRyV1+n6ulbqK1L1SF4CSemNj6QGlJ3d7eympT066jy8l\nDZbU/fqs1YUo9ku1euq0UApUlv0yb546/ZK2Y5AqUuc9dfpaW1vpnZI2NaoWUvdKvRSjo8CTT5Z7\nFHaYmDA/prpKvVJJfWhIrtTzeeJTB4G9pRqHUvf2iwWyWXKARfYLJfXW1tI75dAQ+Vd2AtCeKmmG\nJ3U1fvlL4G//ttyjsENcnnoQVC6pq5T6+LicB3RBRaHrhl7efjGEyn5RkbpKqQ8NAatWxTNelzh0\nqPJIPQiABx9MZlsTE5X7JBaX/ZLLkUhgQ0NlkTpdLLupiXzPk/rICLnOo1wLcXrqLP9MT5NtNDVV\nqP2yfft2rF69Ghs3bkRfX1/Ra5/+9Kfxpje9CZdffjnuvPNOBBbT21SkPjERrtRFBzCbJTs6nzce\nTqKgSr2xsXJIPZcD/vqvk5nJaGNhpAVxFUppkRSorPQLLZJmMuR7OgGJgj59iHhAF5RL4rZfaIuA\nTKYClXpvby/27duHF154ATt27MAdd9wx89pvf/tb3H///fjFL36Bl156Cc8++yx+/OMfGw9gfDxc\nqbe1yZW66AKQ9WtIEyYngZMnSaP9SlLqdJzT0/Fvyyv1UlDyAypLqbPWC0Cua1apUzsjisAZHyfb\niDv9QscKVKCnvmfPHnR1dQEAOjs70dvbi+y52+vChQtRV1eHM2fOYHBwECMjI3j9619vPACdQmlr\na6mnpVLq9GeU+NOI/n5gyRKgro7MrpucrIw+HrpZahfwSr0UlUrqbJEUENsvbW3RkmDZLNlGHEqd\nFYgsqVPBmYTI0YWS1EdGRrBkyRIAQFNTEzo6OjB67tPNnz8fX/jCF3DZZZfhwgsvxF133YWLLrrI\neACqnLqtpy7r15AmUOsFII9xlNjTDkqyntTVoKRucqPWuWFWKqnzSp0n9dHR6PZLOZR6TQ3ZJhWZ\naUCd6sWOjg70n+tKlMvlMDY2ho6ODgDAb37zG9xzzz04dOgQMpkMrr76aqxZswZXXnllyfvs3Llz\n5v/d3d3o7u6e+Z6mX0wLpar0SyUodZbUgYIF09BQvjHpIGlSr4QbnQjsftI9prr2CyXHSiJ1djYp\nEF+hdO5c4MSJaGOlCAKiwNvb5aQOFCyY+fOjbW/37t3YvXt3tDdBCKmvXbsWPT09AIgVs2bNmpnX\nDh48iLlz56K9vR1BEGDBggU4efKk8H1YUuehk34ReeqVbr/Q5AtFpfjqullqF8jl0r9PgqBQ/GNB\nz+WJCX1Sr2b7hZ1NCsjtl6iF0rlzSZM8F5iaAmprS/mHJ3VXxVJe8N53331W76Mk9VWrVmHLli3o\n6upCLpfDrl270NPTg82bN+Md73gHfvjDH6KzsxN1dXW46qqrsGnTJuMB2Hrq9CSpZPvlkksK31cK\nqc8GpT46CvT0AP/7f6t/b2wMeOMbgd//vvQ1up9MjqmOUmfTL+3tRNzk88QGSDN07Je0FUqnpogt\nyk9+FJF6mm6uSlIHgG3btmHbtm0z3z/wwAMz/3/QQWBZJ/0is186OsQHkF4caVbqhw8D119f+N6T\neinK5amfOgU8+mg4qY+OylUhHbeJ6jRV6nV1JFo3PEyIJc0QFUrPnCl876pQ6prU6+rCST1tCZiy\n398pcdfVFTws/jVZoXTBArWnnnalvnx54ftKIfUk7ZeJCbKdpFNBuoud097dovkQUUhdt1AKVI4F\nU4mF0slJotR5/onLfnGF1JB6JlN6QMM89QULKtdTP3ECWLy48H2lkHrSSj2pbbGgq+eEbZeOT3Tc\nbEhdp586WygFqofUXRRKwyKNTzxhJhBUSr2lpfB92uyXVJB6czP5v4zUZUpdZr9UglLP5QpTpoHZ\nTeqvvir+uYo04wQlmzC1zhZDeeRypMjmlTqBbk5dR6lPTooTLmFK/c/+zGyJQUrqjY2F1gCAt19C\nQYkbKC2W0tdaWsgBYx9zqVI3jTTu2wd88Yvuxm8LPhVRKe1345h8dPXVxMfmUS5Sp+fg2Jj691Rq\nPJcjBOOa1NlCKUBidJVA6i4LpT/6EfChD5X+nCp12ZNOLmc2y5zaL5lMsbD09ksIWFLnJyBls+Qg\n19SQ19iLTMd+ESn1X/4SePppd+O3BZ9JrzSl7tJTHxkRE2g57RcgulI3JXVd+4VX6pXQ/8XljNKh\nIfGNLEypm5I6VepAsQWT9vRL2Umdpl8Auf0ClFowOvaLSKkPDKSDPCud1F0RbT5PjrmMGNl/k0Ka\nlXql2i8uC6Xj42LBls0WyJafth8E5JiYWLKTk3qk7u0XDrz9IiN1vlgaptSbm8WkPjhYfvKkJ1wd\nEyitFFJ3bb9QAhUtOl7JSn1iIj6lPtsLpePj4mub9mQXtdyg+9RUqdfXk/97+8UAOp46UDwBaWKi\nsFi1zFNfsEB8V04DqYvaAVRK+13X9gu9sFWkXslK3WTs1azUXRZKx8bkSp3Go/l9aNO5VWa/jI15\n+0UJnfQLUHynpLNJZU2wJidJASmt9ouI1CtFqbu2X+iFLVO77DaTgi6ph0Uabe0XU089TYQig0mh\n1NZ+USl1eoxMC6UsqXulromwQqmM1OfMUZN6mpW6qB9IpZC6a/tFR6knbb9EjTTm84SY29rM7Zew\nbp2Vmn4xKZSGXQdUqfOZc1qfq68vvTHakDpvv3hPXRM2njo9QUQHDyA/Uyn1ckcHvVIvQMdTT6tS\nl9kvNApnOjuS1oJM7Ze0p1/oRC76RA5EL5Tm88V/DxSe+lVK3aRQapJ+8aTOwCb9Qkld5J0BBfsl\nrUq9Gkjdtacus19aW8tH6rZKPZcj57Itqcv2LbvoNEUl2C/06YLtZsmSej5fmKWpU1uif8df36xS\nd2W/UKWusl/ojTgt12/ZSd2mUKpjv1Clzj+i6ZL6E08Af/mX5p9HB/SiZ2FK6iMjwMc/XqpW4kaS\n9gv1pcuRfslk7JU6vWmbkvrUlFqps4tOU1QCqfPWC0A+w9QUSYJRMq6t1cup0+PCP4mrlHrUQqnK\nfslk0mXBpI7U2QNKF54GxEpdZr9MTpK7vmiatm6htLeXNN2KAy6U+uHDwGc+A9x8czj5uERchVKZ\n/dLWFk0BPf448O//bvY32SwRBVGUug2phyl1XqUDlUHqfJEUIERI1TpLkrr2CyBW6i4Lpbr2C5Au\nCyYVpE69NlWhlH38oSeJyn6prye/w97NJycJAeqQxIED8XnvLkh9chJ4wxvIwtU33phcnxvXXRrD\n7Je2tmg3kH//d+BnPzP7m2yWFNptlTothNso9ZYW+efli6QAETcjI+laI5OHSKkDBVJnb1a6hVKg\n+Jyfni4UNkW84Mp+CQKyfbahF5CuWGMqSN3WU1fZL7S5PUvqdLUkHfLcvz8+UneRfsnlyEXxz/8M\nvP71wDveIVa7rpG0UjfNevM4csTcospmyWzlSlDqNTXkWkiLShRBpNSBYqVOP5euUm9qKr62qTiU\nrfcbtVBK7ZfxcTLG2tri3/X2CwMTT90k0kiVOnsQBwaIAgs7aYKgMpR6fT05ub7yFZKA+PWv3Y5T\nhEqzX2xJXUep65C6aOzHj4vPrbD0i4jUgfRbMGGkTmeTAvqF0sWLi69tlitcRRpFbQJE1gvg7ZcZ\n0L4ftGgYptTpQdTx1EX2y+AgsGhR+Elz7BjZRppJnX2Pmhq9mxUAfP7zwGuv6W+Hx+QkOSZJRRqj\nFkr7+82fYMbHiVKPq1D60Y8Cjz1W+vOwQinfIoAi7aTOLzpNIbNfdAqlixYVkzr10wG5Uq+vt8+p\nU/tFReppOQZlJXVK6DTqZJpTD/PU+eb2LKmrmuUfOAAsXBgvqUdNv/A3BtEaryL8y78Av/mN/nZE\n221pid9Tpw2Yoij1fJ6Quq1S17FfRPYQPTYyghoaEo/Jxn4B0k/q4+NiIoxSKF28uNR+YZW6KP2i\nU/xmIbJfZKTu7ZdzYA8E4HZGqUipU/ulpkZdWDpwAFi1Kj6PWqbUTf1XqiKA0n0nw+hotLRMLkeO\nhUv7pa2tdF9PThJrqanJntRPnSIXpq2nrqPURa0AwpT6+Lj4M4UpdVGhFEg/qfPXOUWUQilvv+go\ndVNS9/aLBdjkC1B8EUxPky/2Tmkyo1Sm1Nvbw0+c/fuBK66oHPsF0C/KjY1FI3UaF3VJ6vPmlZI6\nfZppaLDfFl0U2vTmbFIonTvXfPLR2Ji6u2i1KXUTUtdV6iL7hW5Dln6xUeq69ktLS7LRYhXKTurs\nwWYtBJpRp9aMaPKRyn6pqxN76vPmhRdjqFJPc/qFV+q69osLpe7afpk/XxwLbGwkn9FWqR85Qm7i\ncRVKbZW6LFYbdsOUkbrr9MvDDwP/9E/u3o8lXBZR7Re+UKqj1KOmX2SkHmXBbNdIHanTHcO/Zhtp\n5NMvVKmrDkA5lLpp613+PXTtl6hKPQ77RaTU6Y0vilLv7wcuuijeSKNMqcdhv8hIvbnZ7bn6k58A\nfX3u3s+l/UJnoS5YUCzY2BuHLP1iY7+wSp2SOp9RBzypz4C/g7PExJ8IoslHNumXMPslmyVksHJl\n5dkvYUo9CKIrddf2SzYrJ3UXSv2ii+zsF91Io4jUwyYfqZR6mP0iSpE0NbltF9HX53YyWxipmyh1\n6p3zcWUdpd7WVijA64CfUaqyX5qakpknooNUK3U2IaJq6MUnWVTpl3nz1AT6n/8JrFhBxhUE8czU\nc5F+sSmU5nIkEeLCfnGp1FX2S5RGZ0eOABdfHJ9Sj1IoVXnqpkrdJaHk8+RJ1UTRhsGlUqezOflr\nm1fqovRLQ0PpCmoqsKTe0EC4YGDA2y9K2NgvQVDIvdbUiJMsqvRLmFLfvx+47LLS8bhEuQql9GR2\nYb+49tRlSr0c9gs7pnxe/ns69ovomMqUOm0TINu3svRLc7M7Uv/DH+SLUNjCZaGUVeqiGaWAXKk3\nNJSudawCK5wyGTLGEyc8qSshSr+wk1HYE4H6hmNjhMip0pXdlW3tlwMHKoPUbQqllMzTZL+oPPVy\n2i8tLeG2ho1Sn54mr6vsFxul7sp+6esj43at1NnrnMLGfrFV6iyp696wWKUOkL89ftzbL0qYKPVM\nhuzMY8eKmwOJfPUw+0VVlDxwAPjjPy4dj0vEodR17BcXpJ6U/cJGGm1Jvb/f3H6hhbj6+vCYGp18\nJCN1UUGejkVVKDWNNLoklL4+4M1vLp9Sp8dbNjmQKnX+2mZvHLJIo41SZ0m9rU1O6l6pn4Nq8pHo\nRGhtBY4eLS4WyRaZDbNfZAcgCfvFVUMvb7/IMTFBbuIXXFCY86D7d7QxVGurel9RpS6aUSrLqVNS\nt/XUZYVSV6T+8stAZ2eynjqr1Gtq5FFlgPw+Vep8+sW1/cLm1AFvv2iBT7+olDpQIHVeqctI3XTy\nEW3kVYn2i86F7cp+STLSaGu/9PcD551HSIJfOk0F9rxraVETgE2kke57l/aLS0+9r4+QermUOqC+\nFsbG5OkXVaSR5QSbQilAzntP6iFQTT7SVeqqA8gq9SAIT78cO0Ze6+gojCcuUo+j94uOUq+tTZf9\nQiONqvSLzbb6+4HXvY7830TJ8q0pwuwXU1IPs19sCqWuPPUgKJB6udIvgPpcpoq8paWwVin7c8Bt\noVTXfvGe+jmYeOoA2am8Uhc9qonaBNAd3tQkJ1DWT+fH4xLlLJQuXJguUg+zX2yV+pEjxHoB4lPq\nNoXSOJS6K0I5coR85uXLy2e/AOrrjhZK6RMYHWdchVLefqH2Iw+v1M9B1ftFptRFhVKR/cK3CaAq\nHVCTOrVe+PG4RLkKpaOjpGeGC/slSU/d1n6hSj0KqdsodX7yEVv0o+9n2vtFtOg0hStSf/llMumO\n2jmqOKcJwtoEmNgvrCJnLZg4Io0i+4X9l4Un9XNwUShV2S/NzeRgTk0V/HRAnn5hi6T09+J4pCpX\nTp32oS6HUt+5E/jpT0t/Tj111/bLkSN29gtLQGEEEKbUa2uJomTPTx37RfR5czlSvOXPG8Cdp97X\nR5ZIpCrYVYMqlVKnbSt0lTotlALFT+ImSt0mp063B8hJ3dsvUNsvtKEXC1GhVJV+oRMGRkYKyRdA\nnn45dAi48ELxeFyiXA29RkfLY78cPQr8j/9R2k9kerqg/IFi8kuL/RLFUwdKBcTYmFxUqJS6LPkC\nuPPUKakDpSGDKFCR+pkzhRsghU6hlB+jTqQxaqFUReq6vZeSQKrSL5Rsg0DtqfNKXUbq9G+Gh/Xs\nFzrphCJp+8VkW7Y59XLYL5//PDmmbAQNKBzjTKb0phQ1p+7CflGpunye7IO2NjWp88d1fJyIC5lS\np3/HRzBl1gvgzn5hSd1E0YZBReqnTpV+Lp1CKVAca9RtE+Dtl5jBH+yamoIyk9kvp07pRxqBgu/G\nK3UZqcueHFxClX5RrcjEgp6kFLrplyhKPQjMZ5QODpI1VG+7TU7qQCkxlct+YRWfSqmriqG8Umdf\nHxsj56Gqu6jonJYlXwA39gtNvqxcSb43KSiGvW8YqfMkqVMoBdSeuqhLY9z2C18/KRdCSX379u1Y\nvXo1Nm7ciD7u+fnAgQN497vfjXXr1uHGG2/EoGFTZ9HBpopTRupBoO+pA4ViKeup65J6XI9UIqVO\nJ13oKmD6OEmhm1OnTys2REkL0CZE++UvA5s3A296Uymps6qL39dRcupBQJR6VPtFRQCsPcQ3lWNv\n2jxB0RqCzH6pqxOfB66VehAQu5Hi5EnysyVLyPcmNoUKU1OFc5sHtZr4z6VbKLX11G3bBKhIPWzS\nVJJQknpvby/27duHF154ATt27MAdd9xR9PrWrVvxpS99Cc888ww2btyIgwcPGm1c1BOCXgQyUgf0\nPXWgcOB17ZeklLqo4GViNdgWSlta7FdpoftV9+SdmAA+9zngYx8rnd0LFF+gvP0SRakPDhKPlt78\n4/DUKXGzT5fsa2FKnT/OtCNoXZ2YlEZGxGQCFDx1E5X40kvAJZcAzzxDvqfWi2hRmiiQqXSgcOxN\nlDpfKKXnVFLpFypqREiLBaMk9T179qCrqwsA0NnZid7eXmTPXXmHDh3C0NAQ7r33XqxduxaDg4O4\n4oorjDYuOuD04tYldR37ZXi42H6RFar4dr9pJnXbQmkUUqfjlvWx5/H1r5PFRq64IpzUZfaLjVJn\ni6Si91ZBd/IRW+zmzxMVqcs8dUogmYxYqYuCAxT070zqHAMDROS8973Ab39bbL0A7pS6DqmbeOps\noZS1X5JKv8hurGHjThJKUh8ZGcGSc89jTU1N6OjowOi5PfLaa6/hP/7jP3DXXXfhqaeewve//308\n8cQTRhuXkbpMqdODr7Jf8nnyRavpMqUu2vlJKXVR+oWOi7/Yt24lj8Y8bAulra3RlDoldR31/NnP\nAv/9v5P/65A6b7/YFkrZIikQz+QjlcWiInyZp87OXhTtX7o/ZDD11YeHgbe+FfjkJ4EtW4Bnny0U\nSQF3hVIbUrexX3SUuov0SyWQusDpKqCjowP9/f0AgFwuh7GxMXScm0Pf0tKCSy+9FJdeeikAoKur\nC319fXj7299e8j47d+6c+X93dze6u7sBiCclRLVf6Cww+hgZxVNPg1J//HHgIx8hqRUWtoXSqEpd\n137JZoHf/Q4496CXqP3CFkkBM1Jnz0nVfrJV6rSuIVLqVBWqCn0yUAtGFnvkQVcP27oVePVV4DOf\nIcVsCleRRhWp19QU1DML3UJpW1thbVb2XJJZsg0N5DXbNgGtrWpSj5pC2r17N3bv3m3/BuegJPW1\na9eip6cHALFi1qxZM/PaZZddhoGBARw9ehQLFy5Eb28vbr75ZuH7sKTOwqZQCqgjjaJHpkpIv8jG\nNTIiHgNfKNVtExBFqZvYLydOkMWB+ZsrC5ZA02q/2Cj1MPtl4cLSz8SetyJSClPqpoTCpmk+9SnS\n7+iaawqvu1LqstmkFLSNLguTGaVHjpD/hzX0YieE2bYJuOwy4MMflv9+VL5gBS8A3HfffVbvoyT1\nVatWYcuWLejq6kIul8OuXbvQ09ODzZs3Y9OmTXjooYfwzne+E7lcDu9973tx3XXXGW3c1H7R8dT5\nuyur1G0KpS57YFCYKHUVqfNKnUYiKZHyiKrUTeyX48cJqVPwXfWA4kdm3n6JklPv7y/u4WMyO1I3\n0mii1PnJRypPHbCzX2xInYqjmhrgv/234teTUOoA2ddRCqWsp65TKG1qsrdf2tuBu+6S/35F2C8A\nsG3bNmzbtm3m+wceeGDm/7feeituvfVW643L0i+yQqnIU+eLSiKl3t9fmfZLLkc+jyz+xn7O2lry\nxdsyLFwodVmOmsfx44V4HBCv/XLoEIlNvv/9xE44cgS44YbC683NwOnT4Z8PIGNYsID8XyfSSMcu\nI3XR5KN589TFfVGhNMx+MfXUVTNUgcKckKjQIXXbQilNv0xPl9pXcRRKw5CWVgGpmnwEFCt1Xpm0\ntpLfZ3d0mP2iO/loaqqQPODH4hq6pE5PPh2lDoSrNVfpFx1P3ZTURfaLbk69v5/8zsGD5BH5Jz9x\nY7/oTD4CxKQuI3yZUo9aKDVtFcAqdRGSiDQC0ewXqtSpOKRPqKpCKbvWcRh4pR6GtLQKSDWp86+1\nt5OOfiygIt8NAAAgAElEQVR0SJ23X0RkrRqLa+imX+hFJRqDSJGHjddl+iXMU+dJnY6VV61h6Zfa\nWnKxqlYuGh8Hli4lM1dffRX4wheA1asLr8c5+QiwjzSyxMIqTZlSj8t+ESGJSCNgbr+IZpTyvr2q\nTUBDA7GbdCw9U1JPi/2Sqt4vgLpQesEFwM9/XvwznmBE9svQEPmiXrxICSRF6nSqveixzoTU+UIp\nEP74F3XyEd1mTU040Z44UUzqQKla18mpA+FqnT2PFiwAbr+9mADjnHwEiG0zVfqltZXcrNj9x54T\nMqXu0n5RtR0AKlOpU6jsF0DfgrGxX2Y1qdOeELz6UHnqAFFkLHgrQKTUjx0jB53edU1I3bVHRsdX\nI9jzpqQusl9kJxVdxb6pKXzxBxnYbYZZMHyhFFCTusxTB8KLpez7iGBiTSQx+ailpfQzlbNQKoJL\npa46NlELpcPDpeJQtfA0oH/DsrFfZrWnPjlJ1Aq/01T2iwg6kcbXXitYL0B5lbqq4GVqv4iUelh3\nu0wmuv0ChFswvP0ClJI639BLZL8A4cVSHeKw6ace1+Sj5ubSp4+ohVJTT12nUJqE/fKBD5BJUCxk\n5zEVJnS/U/tFpNTZ/RcExU+2up/N2y+GkF2INqSusl/mzCFWAC2SAnJSFz01JE3q7PZcFkppkRSI\nbr8A4QkYHVJX2S/sxatjv4SRuo1Sr68v2GU8okw+Eil1HfslaaWehP3yF38BrFhR/LOwdBotiNIx\nhnnqlJzp07HuU4i3XwwhO9gqT12EMPuF+nU2Sj2OaraNUteJNALhBSb6mBs1/QKEk7qppx6n/WJL\n6pmMXNXJCqXT04XGXPxrdKyU1EXEA9gVSm089TQodRFk5zFbJAXIZ56YINeJylPn609eqccE2cFu\nbCQnvipvzUJ0V+aVOlCs1MuZflEVvPiJKjL7hRbY2NVi6N/LxutCqbPHROWpT02RCOm5jhIzCFPq\nMgsjzH4Jm7Vo208dkO8rmRqn+4iqSf6GRO0XG6Xu0n4JK5QmpdRFkN3E+Zs3vemeOqVW6jyXmBRK\nXXvqjz8O7Nql/542KBupyy7ExkYSP2xslM+MZBE2o5SqUx37pVI8ddl7qE4qV0pd1Z+E4uRJkkLh\nbzpxpl/iUOqAuVIXzfRlCT8IChO4eFJnC6WiLo1J2i9JtQkQQXbdsUVSijlzCKmHKXWe1HULpa7t\nl5/9DNi7V/89bZBKpT40pH8ihM0orasjBzxNhVLZxSki9Zqa0jHIvL64lbqu/SLy04HSVgEm9kvU\nQqktqauUuozU2ePLvsYWq0XpF1XvF51CqS6p5/PkM+lEGqOu5OPafuGPc1sbEREqpS4i9XLZL0eP\nulvQW4ZUkvrgoP6JEJZ+AciBD1PqExPuSf1b3wKef774ZyZKfXSUKF5dpa6KYLpQ6mFNpyhUpM4r\n9bCGXkB0pW47oxRQK3WR/aJS6qzS5G9UUQulJp762Bj5jPyTFAu6WEdUUePafuGVelsbqd+wx58X\nejakns+TG5pqH/HQiUD397tZJFyF1KVfmprck/qcOcnbL0EA7NgB7NlT/HNT+6Wjw8x+SVKpy+wX\nUZEUKF6pBpA39KLvq5pXwMKl/cLbBTZKXUbqrNJU5dRtZ5TqfsYw64XCxQSkuJW6rv3ChyfCSN1U\npQN6wYqqJ3WZUh8YMCN1lf0CkAOftP3y618Dr7xSSgimpC5S6jb2S5LpFxOlLrJfeFXqqlCqYyXY\nKHX2uOkqdVVO3bZQqqvUdUndxQQkW6Uu89RN7Bd6vG2Uug2p6/BFf7+3X0IRFmkESu2XJNIv3/ym\neEaiDanzNyDbQqmL9Iuu/cLPJgX0C6W8Ko1qv4jWERVBNMvZVKnz5Kur1MMmH7m0X8KSLxQuiqW2\nSt3Efjl5svj419SQL5oSE6Vfwp5ATDPqdNwqvshmgTNnqliph6VfXNovK1YAy5cXvo9bqQcBIfU/\n+ZNSQlApLl37xbZQmmalzj66ipS6ipDDCqWAngVDH9NZH1W2r2w8dfbGqsqph/UuEcFEqYfNJqVw\nEWssR6EUKN6HSSn1sGNw9Cj5typIXfTY67JQGma/fP3rwLXXFr6nSiifLx4Pr4Toe7O/p4Pnnycn\nztVXi5W6SfrFxFPX7W4Xt6fuwn5hP5+O/eKC1EU3B5eRRr5QqrJfbCKNcXjqLpR62LHhYVIoFXnq\nQHlIPUwE9veTLrNVYb+opvzyoCuTuLRfeNBIGft3ovGIomc6+OY3gT/9U/HJY5p+MS2UxtkmIMz3\npZAVSnXTLzyBRbVf+PeXQXQORC2UsseUVZqqnHrcy9mZeOqVoNSnp82Uuk6tIA77pb8fuOiiKlHq\nog+h6v0C2NsvuhMGeAJVPTmYTOqYngYefZRYLyJCcJF+iVoopf6r6ROIbpfGOOyXKIVSQF+p8+8T\nZ6RRllO3sV9MPfUklXrchVKg9OdsXcKmTUAcSv3o0SoiddHJpiJRwK2nLoIJqZv46k8/DZx/PlmB\nxwWpu8qps0q9psaurbCO/ZLPE49z0aLS11hSp0uQsWo3TqVuS+ouJx/xhVLZeWtTKDX11HULpeVS\n6ib2C6BW6jZtAkxbBNAxqI5BVSl10QcdHxefpKakLppRqnMweLJ2RerUegHiI3XZjUuVk2WVumxs\nYdCxX86cIRea6DOypM533FPZLy4KpTqkJ1L8SSr1qIVS1566i0ijyzYBMvsFMPfUw25Wpi0CVOOm\nYEk96kxdFcpK6rLJR4BalbBwpdRFM0rpOExI/d/+DXj3u8n/RZFG3fRLEBQ8dd1Io26bAMCO1HXs\nF5n1AhQ82iAoPf7sDYlXu0kWSl176jKlHpZTj1Opp33ykemMUiBa+iWfJ+vbsoirULp0qZuZuiqU\n1X6Jw1N3bb+YtN8NAmI90NWZZEpdlX6h28pmyfctLe4besnGFgZ+kQwR0cqKpPRv6urIGHkiTqv9\noqvU6fhUOXXd9At/wwwC9zn1tE8+mpwsrfnIZpQC0dIv//ZvhadrCttCaZj9csEF5ByI04JJnVK3\nIfWwSKMIcXjq2SzJONPPYGq/sORAfU/R9qM29JKNLQw6XRpVSh0oWDD88WcvZFGkMYzUw84XHXvC\nRKnzDcds2gSocursvp2eJjaVqg9JmiONpqSeyYhv5KpCaZhSV7UJeOKJ0s8ZR5sASurNzfHGGiue\n1G0ijUA8pM4ubg2Il0PT9dRHRshFJdq+bU49qlLXmXxkQurs/s5kCheFSfqFL7jKoKNkTXLqtvaL\njVIPU+lAPIXSqJFGnScMGUTFUpX9IlLqbPpFpNSpr/3kk6U3RNf2y+goOT7z55v1IrJBxZN6mtIv\nvAKKUii1Ueq6bQJkYwuDziIZshYBFJTURQRKx29iv/AFVxlce+q6hVJWxYf1fpEVSsOaeQFm/W2S\nUup0H4kWWQ+D6LxXFUr548aen3z6pa6uYAP+4Q/ET+evG9c59aNHiUqnawTPKlJnJ6PowJbU40i/\n8EqdKk/WGzQldVFm17ZQ6kKpx2W/0PGLSF2l1HX8dMC9/aKr1OvqCNFOT4d3aZRFGsOaedG/qalR\nLwZOkdTkIxvrhUJ03qsijSaeOlC4Yf34x0B3txulrhJV1HqhY51V9gs9OV0tkiGDSKnLIpa2pE4/\nB3vC6KZfWKXuuqEXEJ/9oiqUAmpSl9kvKqWuS+q69ouLSCM79kym8LptP3VdC0PXV09Kqdu0CKAQ\nnfeuIo1A4bM9+STwznfGb79QpU7HWpVKXXUXb2ysTPuFJ3WgNNao2/tldJScsHV1ROnTjnOAeaGU\nxiOTsl+ikLpMqatIXedc0bmQRO8VVamzr+v2U+efgsIy6hS6vnpSkcYoSl10Lsue7mprzQqlQKG3\n/09+Qkidt65s7JfaWnITFz0tsUp91tkvQDRSj6NNgK2nDpSSgqn9wiq9sPeQjTWXK/iIsnHpgLdf\nXJM6tV9Mcuq6arBcbQLY13X7qdsUSgH9WKNJoTSqUo9iv+gUSjMZ4F//tVRM6Sj1554jk/suvJDs\nc9lN1gSya7C/n8w0B2ah/QKYkbpL+yUOpR6F1KkHzo/BtFDKq3TRuHQQ1iYgCPQLpSJVbFMoNfHU\nbewXUZ+cfL5YPNDjFgT2Sl01+UinUKr7GelTWxJtAlwrdZH9AhClzRfKdUj9e98DNm4k3/M3fZs2\nAYD8GPCe+qxT6k1N0ewXnYMRx4xSGamzasdUqYvGYKrU+TgjHZfryUfDw+Tn/A2EBS2+hXnqujl1\nE0/dRqmL6iJ0/1MiYRfhENVMdDx1fjk7Xqnr2i9hn5F2QNVZezOqUrdpEUChWyiVgb0x8ukXgFwP\nP/lJgdR5MrZpEwColfqsJvWkPPW40y9AdPtFNAZTUhcpdVELgzCw9ovIUw+zXgB1pDHO9IttP3Wg\n9BiK7BBa2HOl1G0LpWFKXddPB9wUSqModXb/0FWpbOavyJT69DRJvgCl50cc9ov31GOeUcqeNPm8\n+G5Ofy/K9GsRIZiSOn8DMrVfXCn1MPvFhNRNCqVh9ovOuWJrvwCl5CayQ+gxCiN1maeuWnhat1Cq\n46mbkHpjIxmHTkxSBJf2C22boZt5D7Nf2tqAq64qLHMpsl9slbqO/VJ1nrqooROL228HLrlE771d\nzCilJCKawBKHUjdJv9AxsAQgu8hVPTNceOph9supU8DCher3sIk0lrNQCujdmOl5Ijo29LiarHwU\nV6RRt0gKkOshilp3WSgVncMqhKVf5s8HNm0qfC+yX2w9dZ4vhofJNUm5IW77xWLY5uBJfWqKnDCy\nndbTo//etbVkh+Xz5C5uQ+ph8crBQb2x6EYaZaqL+pzT03aFUjYpwxIdP/EIiJ5+Edkvou3wYEmd\nL6jK7BdXOXVXpC66MatIXWa/qHLqcRVKTZQ6UKiBsAu3UwwMkM8lezpzqdR1jzNFmFL/xCeKr6E4\n7Rd2NilAzqdTp8zfWxdlUeqmB0gF2vyHnRLsmtST8tTZcdl46rLxulLqYZOPdI5rHDl1V+kXmZXD\np0DClLqI8IeHyU2bEoUqpx6lUOqa1FVK/atfJeQog8sZpSZFUkC9SAZQmNhH4dJ+4a8/1nqh26o6\n+8UlqQPFHqQtqcuUkEnr3ag5dTquiQm3pO5CqQdBeM9vU1IXRRpFxJhkoVREQvPmFT+tyQqlKqU+\nMFA8zrB+6nHl1G1IXRZrHBggKlQGl4VSWZxRBlVDLxF46yqK/cIfAxGpl7VQun37dqxevRobN25E\nX19fyetTU1O48sorsXXrVul7xE3qtkqdkl/cSl030ki3F6bUVZ9RdFK5UOp8+9eoSj0s/cLuIxeF\n0iikvmABWdGJQmWxyF47e7b4GIg89aiFUh1P3cZ+kSn14WHg2DH530ZtExCn/cKDvyG6tF94Uo87\n/aIcdm9vL/bt24cXXngBzzzzDO644w7s3bu36Hfuv/9+NDY2IqNok5ckqevmS1klkKT9EvYoTS92\ntlDKP4raKPWopC5qVJUG+yWbDffx2fdWQZfUVUpdZs3wSl2UU08i0mhSKAXUSn1oKJzUy1Uo5SON\nYZwQt/3yutcVb6ts9suePXvQ1dUFAOjs7ERvby+yzFnz6quv4rHHHsPWrVsRKHp+VoL9kqSnrrpA\ndTx1WfxSNl4XkUZ+m7b2C+254Sr9kkROff78UqVuar/oKHWZtVVO+0VHqcsu/TQXSnm4sl90PfWy\n2S8jIyNYcq603dTUhI6ODoyeO8JBEOCv/uqv8A//8A+oDZmexp9oUR7LRIhaKJXNJgX0ST2fJyTJ\nq6AohVI2/cJHGk3sF1dKnd2mrf1CSULkkaY1/bJgASFlCtNIo46nriqUumzo5bJQOjRE9qlMybuc\nURqlUGpjv7hsE8D2fQHKbL90dHSgv78fAJDL5TA2NoaOjg4AwLe+9S2cf/75eNvb3oZXXnlFuZGD\nB3di507y/+7ubkxPd1sfbBF4UjdtE+BCqY+MkIPFT44wiTSy43KdfuHz4+WyX+rqyBhPnzazX8rV\nTx0Qe+omM0obGoAjR+JX6k1NZL+qMDwMXHxx+HtRqHqqDw2Rf48dE98ooip19njZFEpV6Rceokij\nK/vl9Oni608mMHbv3o3du3ebb5SDkv7Wrl2LnnOh8T179mDNmjUzrx0/fhz79+/H+vXrcezYMQwO\nDuKzn/0stm3bVvI+8+YVSB0Avv9990q93PaLyHoB7JT6+Hjx00zUQqkLpe7KfgEIAZw8KU+/xDGj\ntL6+0IhLdtM38dRFxB2m1HlSl+XURUpdlBPnkbRSHx4m5/yxY+LJglFJfWCg8H0SSj0u+2VggFh4\n7LZE1153dze6ad8CAPfdd5/5ABBC6qtWrcKWLVvQ1dWFXC6HXbt2oaenB5s3b8ZHP/pRfPSjHwUA\n7Nq1C3v27BESOpCMp17u9IuK1E3SLw0N5FG/tbWg+l0odd5TpySnu79c2S8AIZVXX5UrdVGkUVUo\n1dlmJlN4xJYVCmU3CJNCqUzFDwwA5x5yhZ+J76ceV6TRtFAaptQvvVReLHWZU08i0shabC7bBJw9\nS2KxFGWfUbpt27Yisn7ggQdKfue2227DbbfdJn2PNEYaXadfZAqIVcTT04RIVSUISursheeiUCrq\nQ029PV1Sd2G/APIlyOJs6AUULBgZqcnOA1GhNGqkMayfuq39EodSP+fACt/r2mtJzx8RXObUoxZK\nddIv7Od01SZgYoK8F79ATdU19EoDqZfDfuFbtsrGdeZMsbIWRRpN7RdR7M/EghHZL65JnfXUTXLq\nuueSSslOTZGbrmi/igqlUScfqXLqdN/SVEk5c+qySGMQkPe65JJklLqp/RLWpZFHXPbLwABR6ew1\nX5UzSl2nX+KONOp0adQl9TDFRUldpdRdtAmgY9Nt1iSyX6J46oB+pJFuSxSdMyV1GenRBJTohtve\nTgiMLiloE2kcHi4+BnV1hSc3oLgwV1NDvuhr5VTqskjj+Dj5nK97XTyk7mJGqWmkkU+/uCiUnj1b\n7KcDKZhR6gK5XPEFGSXqJAKrbKan7dIvsosmDqUeNi4Rqcse1XmI2hqkUalnMmLyE9kvfH8ffpu6\n55JKyaqERm0tOba0cKeKNMpeA4rfn35+2RMmrzTT1iaAnu/nnRcfqcfZ+4WHK6XO3xyoUmfR0EB4\nyralcRgSIXXRo1Qc9gt9hFXZG+yYkvDU2UhjFFI3Ueo6bQIAM1J37ak3N5cep6YmMh6RDSKzYEye\n+lSkF0ZArK8uU+pjY0RY8DUT+rv8MWA/E08i7JOQSUMvnda7LpQ6S+oqT932OndRKKVCz2ZGqSv7\nRaTUadE+LrWeCKnzd6+4ZpSaZEvZk8bF5KO4lXqUQqko0siPLQwu7Ze2NvH+bmoi+1FUd5AVS13Z\nL2GkzvrqMjU+PCweu0ipAwVhQfvqsHMc2JumK/uFrk+q01aBQhZppCJmyZJk7Jfjx0sVrwr0/Jya\nIjfZsMU14rJfREodqBJSZz9AnEpd90Dopl90uzTKSJ0WRWSLEvPQSb+YFkpFkUagvPaL6PcaG0k3\nRBGByZR6kqROlbqsUDoyIj6+9Gf8jZXeqETnraknDITbLybrk1LIIo30fF+yhBCuqN7hqlA6MgI8\n9xywfr3+39P9Z7LvklLqQLwJmKpQ6rz9ogNd+4VeeIrWNgDkpF5bS95DlOqQbU8n/VIOpe7afuFB\nlbqI1GVZddNIo4z0wrx5ltRVxVDRcZHZL/QziQiEt19cKHVTPx0o9OqRvVdTEzlX2XQQRZTaGSu6\nfvhD4JprzJQ6PT9tSd1Vm4CqVupxk/rUlJlS1yV1WqSTReoohobkFwwlT52CF51CL1PqfF9z0d+z\npD49Tbars6IPi8OH1f1meKUetkQhCxWpy4rWIvuFfjYdwgPiVeoNDYToRGOR2S/0vBIdT5tCaZin\nbkPqfJyTghUxsmKpq0Lpv/4rcOutZn/PCj2bvjmu2gTIlHqcscaykHockUZT+0WX1AE9X51OmRaB\nJfWonjpVdbJiML+vaZFU9PsqUv8v/4UoJIqwNgGTk8S31FE3KvsFEO8j0Y1VFUMUIQqpz59fIDdZ\noTROpe6ioZdpkRQg5/TISGn9hBUxMl/dhf0yMUHOw1tuMfv7tNgvMqVelfZLHJHGcpK6zH4BzEl9\nakoeaQxTHvxYVYUxFamfOEH6s1DwY+eVusnTl0qp08/AQ2S/mD7xqUjPVKnb2C+iQqnsvLUplIZ5\n6sPDZi0CAHKjnjevVK2zIiYupZ7LAT/9KXD55fJ1UGWISuqu2gSolHrVkXq5C6W6vV+A6KROY426\npA7IlXpYPIvf1ydPAosWiX9XReqnThV3/OO3y3vqJsf0qquAv/zL0p9T4tK1X0zPozClrnov3lN3\nqdRlhVKT3iV0O9msvP5jY78Apb1vgFL7hY81qmbo6oCe89/9LvCud5n/PUvqOmMQ2S8u2gSoPPWq\nsl/iijTaKPUgcEfqOp66C1I3UepHjhQ36BeNS4TTp4tJnX9CqK0lMx7prEeTY3reecCf/mnpz2tq\nyPHTTb+4JvWklTr9TCICsVHqdXVkH8r65NiSekdHKamz7yVS6qbWGA/arfSxx+xJfWqq/PaLV+qW\nsFHqNLs6Pa2eUQq48dRHR/XTL0CxZcJuP+wz8mPll9LixyUi9bExcoxUSp0WkKmadHVMm5rMlLrJ\n430U+4WdfCSLNNKMPQ9bpW5K6oD6M0ZR6nyfdlapizz1KNYLQD7vqVNEkKxYYf73pvYLtT1pK4i4\nc+pV6amXm9SBwkWVpKeu0/sFKFbqrFUUdpLy+5pfSotFW1thoQMW9ALmSZ3fLqsm4yZ1kVI3Lbir\n1JFOpJEtlIqU+tSUOqdu4qnb2C90GzJStymUAnL7RaXUo5I6/bw2Kh0oDk/o7LtMpvjacdUmwKdf\nLGEzoxQokLpqRikQTuq0vabsPVzaL6aFUpX9sngxUUM8RKQu2i6rJl2RemNjfIVSFamzyweKoDP5\niI6Th036xcZ+AeRKPQjIuWBaKAXk9ovKU3eh1AHzKCOFqVIHis8PF/ZLPk9ufqIFTrz9EoJyK3V6\ngsv8QxekTgktrPBjYr8sXkxSLjxOnSLFVb6HuEpNulTqMrUbtVCqynGfOVO8iAUPar/QmcEyUlfl\n1GW9X1RKXXcmMgX/GX//e2DnTuCyy4g/vWmT3vuwCLNfZEo9yvlQXw/8n/8DvPGN9n9vUigFionW\nhf1CO3OKbg5Vab+UO9IIFGwNHVIPi4rJrBfAffrFlf0iI/XTpwkJhNkvcSh1E/vFRqnLjuOZM4S8\nVOOqr5fXRnSUOn+O0RuVSqnTmY1hvUso2M8YBMC6deQm/Y1vAK+8Aqxdq/c+LET2C+vPL1pEzhXq\nRwPRlToAvPvd9oVWG6XO3hBdKHWZ9QJUmVI3mXmoC5s2AYA7pa7y0wE7pe6qUBpmv8iU+iWXFJQp\n3W45PXWR/WJKHKoLKYzUgcIEJJFSp/tG9pTx1FOlPVd0CqUm1gtQfK0dP04+7//8nyRGakuQIvuF\nPefr6si+Yec1uBZupjBNvwDFN0TbNgF1dcR2mZ6WF0nptqrGUzeZeagLm0gjULA1XJC6qgBFSd0k\n/eJCqU9NEYI+7zzx77a2Fjr3sTh9mtwIaEyPbjcJ+0Xlqcdtv4SROlWspkodAJj1hGegInV6TpuQ\nElD8GV98EbjiCnsypxDZL3yShvfVXSj1KHDhqdvYL5lM4XpVKfWqsl9cq3TAjf0SJdKoo9RHR/XT\nL3V14tmbdKFo3ULp8eNEZcluoJmMWK2fPk3+rqOjcDHPdvsFKCZ1k0KpDKqcugul/tJLwKpV+n8r\nA2+/5POlC1jzvnpaSF03/QK4sV+AwjUYptSrhtRdJ1+AaKQ+OkoOnqodaVj73TBP3dR+aWsrVld0\nUk4uZ1YoVRVJKUSkfuoUsHBhMamnwX6Jc0apCanLIo10nLrQiTSaKnX2xuWK1Hn7ZXSUbIe9Zvis\nerlJnT7pTEyYFUpZRyHKbNhsNtxTrxr7JQ6lbmu/NDQQlR128iXtqYtiZ3QMJvaLqkhKYaLUVRNk\n4rZfXCh1mf0SBIXPrIJrpR5mv7hQ6ldcof+3MvD2i0jEpM1+yWTITWdsLNlII1AQgSql7u2XEERR\n6oODbkhdx1PXIfXGRjmpywhANlZVkZRCV6nL7JckZ5RGLZTKVvEZHy8sMaYCWyjl9wWtE9mQuqpL\now2pj4+T933lFeANb9D/WxnmziX7jd7ARSImbfYLUEgrlct+MU2/bNkCPP203TZZlIXUXR/sKKQu\nW5SBRVSlbhJpvOQS4K//Wj6GsPeoqyPKc2rK3n4RKfWkJh/FmVNfuFA82UrHegHI75w6JW9U1dho\n56mrlLpNoTSbBfbvBy680M0xoZ0a6cLbonYDabNfALJPTZU6bYhmm34B9D113n753e/IORoViZA6\n61XFpdRt0y/Dw9GVuq6nrpN+aW8HPvxh+RjCCj9s9V3Hflm0SKzUdeyXODz11lbxzE4X9sucOWS8\n/MVkQurHjpH9L0qUyKwjGVQ5ddtCKb3WXFkvFKyvXklK3cZ+yefJjUx3bgAPemM1Veo6IkwHVWG/\nUHKxaROQtKducoGKxqAzQ47+7pEj5kqdRjzb28tjv+zcCXzwg6U/d9EmgKZ92Dw1YEfqIpgqdZ3W\nu6bnDL3WXnzRTZGUgvXVRXZj2jx1wNx+oUQbxXoB9JQ676kPD5MnQBWP6KIqSD2q/WJK6r/6FXD0\naOF7HU+dRhpNLnp+rDr2C1DY3zaF0tOnyQWcyZTHflmwQKzUXdgvALEJ+CcTXVKfP58cdxnJuiR1\ntlBqk1N3lXyhYGONoifT5cuJiKD2VhwpN1NQUtflBHrdREm+AHaeOhVgUecUAD7SaEXq27cDn/tc\n4XuX6ZewMeh8Rlapm5I6LZIC5bFfZJB1aTRVg6oaQhjClHpDg9ucum2htBz2y9y5wHveA/yv/0W+\nT8MYSikAABOSSURBVINSr6srn1LPZs08dVfWC1AlSp2NNJq2CTBNvwQB8ItfAP/3/xZed5lTDxuD\nboJmYIBsM4ysREqd/g37yJ3U5CMZXCl1EambFkpdKvWw5exscuqHDpHrbelS/b8LA3suyPqy/9f/\nCjz0ENl2udsEAPb2S5QiKVCINJrMKNURYLrw6RdDpf6HP5C/Gx4mCQMgOaVOH9V17Jff/x44//zw\nx7lFiwhR0RWMZEpdZr+49tRlcFEoBaKTOpCsp26j1Pftc9MegAVrv8jO9ze8AbjySuCRR9Kh1E0L\npZSnbFsEUJjMKKW9lbxS52BL6nS1Gh1Sp+P/5S+Bzk6yujlV6y57v6jGYFIo/f3v9e78dLITjaux\nSj1N9ouLQikQjdTnzCETWlx76qoujTaF0oMH3frpQLH9olpBads24P7706PUbdIvLuyXoSHyPny7\nZQo6S9xkTokuqoLUk5xR+otfAG95Sympq5R6fT05iCMjbtIvOkr9d7/Tv/OzRMcq9fb2wkSWpAql\nMqTBfslkyOO0K1IP66duUyil+8M1qfPpF9n5fv31ZOxPPpkOUjcplLL2S1SlfuwYUemqpyXWgtFJ\nqumiKkg9Sfvll78kpN7dDfT1kYOns/ZjSwvx2KKmX3QLpb/7nf6dnyU6VqlTEpPNoiy3/eKqUKpL\n6gD5PdkxnDdPvMqNDGE5dVv7BXBbJAX07BeAnDPbthFxkBZSt7Ffonrqx47J/XQKNgFT0fZLGtMv\nujNKg6BA6o2NZBWZb32L/D9suy0txOJIqlBqq9T5JAi1YJLq/SJD3PaLTvoFIOQmO1++/nWiVHWh\nE2m0mVGaydivGCQDH2lUiZg/+RNy7smsh6RQzpz68eN6pE4TMC7tF4ddzeWgiiSfT9eM0oYGYono\nKvWDB8nvnn8++fkttxD/UGcx35YWdRwuDLozSoGCr2qj1Fn7BSiQelJdGmWI036h2XwdLFhQKCrz\nMCWxOAqlzc2k1YRrQmXrK2F2Y0MD8NxzheukXLCNNLq0X3S2l8+T368oT52dup62GaVBEE7qNKJE\nVTrFli1k5p7OLLCWFrKtpAqluVx0+wUoVurl9NRdpV8WLSIzSmnqADCzX1SeuilUOXXbLo1XXw18\n5ztuxsfCRKkDZDJSFGJ0AVqITKv9Qj31kyfJDcCWG3iEkvr27duxevVqbNy4EX19fUWvffGLX8SV\nV16Jzs5OfOlLX1K+D91haYs00rGpQAmVJ/V584CuLn1SZ7dpCkrUuvYLEL1QCoTbL1NTZHrz5KQ7\nshOBV+r5PDkmpudSQwOZsUrTPuPjZPy6ylblqZsijn7q9fXA5Ze7GR8LtlNjmFJPC+g+LYf9oqvU\nx8bcWi9ACKn39vZi3759eOGFF7Bjxw7ccccdM68NDAzg7/7u77B37148/fTTuPvuuzGhaJDCknpa\nPHXZgsCi35uYIMmXt761+LVbbtGzX+jU96jpF53PSD+P7uNvmFKnypZfSISqSVqwdJmL5sErdapg\nbbbJft6zZwttEXSg8tRNwUYaVemXOG+WuqipKRTNwyK8aQHdp+VIv2Sz+oVSl8kXIITU9+zZg66u\nLgBAZ2cnent7kT1X8Zw3bx76+/vR2NiIEydOIJvNYopGIQSIk9SjRBrp2FSQKXUAuP124LOfDd+W\nC6VuUiidM0f/wqMkNzVFHq1ZhdHRQfqdiDoTUuKJ23oBSgulUbbJkrqJ9QLEQ+qiGYy2hdI4sWAB\nucGPj4v786QNpkrdlf1Cz48wpU7tF50eTSZQkvrIyAiWLFkCAGhqakJHRwdGuVUGpqenceedd+Ke\ne+5Bq+JI00ZDcadfTNsE0LGp0NhI7qbNzaWLOLe1lRK9CFFJnY006hRKTe78lOTOnCEnIqvIOzrI\no6ToZkktgqRInbVfykXqf/ZnwMc+ZrddHmE5dZtCaZxYsIC0IGhrs29LmyTKZb9QPimXUlcOvaOj\nA/39/QCAXC6HsbExdDDP5vl8Hh/60IewZMkSbN++Xfo+O3fuxMAA8PnPA8ePd6O5udvN6M8hCU89\nny+1XkxAST3KuocmhVKTOz8lOVFjK1ap80hSqfP2iytS123mRbFoEflyAVVO3bZQGic6OkiqqhKs\nF8Ce1F3YL4C+p97fTwrcu3fvxu7du+03fA5KUl+7di16enoAECtmzZo1M6/l83ls3boVtbW1oUXS\nnTt34vHHgdtuA55/vjLtF0BPkcvQ0kLGaatwTO0Xkzv//PnEdjl6tHTlFRWpU+KZTfaLS+j2U0+T\n/XLwYGUUSYHCjdLUfona0IvyhW76hRZKu7u70d3dPfP6fffdZ7V95dBXrVqFLVu2oKurC7lcDrt2\n7UJPTw82b96M5uZmfO1rX8O1116LG264AQDwyCOP4AKJRExjoTRpUo9ycZrk1Ds7yVJmuqipIWS+\nf79YqcviWUl76qz9EqVh1OLFZDYwkF5ST6NSX7CANLSrNKWuywm1tYVse1JKPXH7BQC2bduGbdu2\nzXz/wAMPzPx/kp8NokDckUbb5ezYf2WoqSEHICqpR7k42Uhj2GfcsMH8/RctIkQnUuojI2RxCR5J\neuqu7ZenniL/Lyeph/VTT1uhtKMD2Lu3cpS6qf0CkHNqeDg5T53aL4kVSl0iTqWeyZC77Ph4PEod\nIITHF0lN0NqanFK3weLFwMsvi5U6kA77JQ2FUpfQyamnTalXkv1iQ+pNTdFJ3ST9MjBAtudiwWmK\nqiB1gByEOEndxM4QIar9wi5nF8dMPUrq/MlFW/PK0i+VXihNg1KvJPvlxInKs19slLoL+yWsuVtz\nM/Cf/0nmk7hMEyXS+wWIN9IIFHonx0XqUeHKU4/rcXzxYlIQFSVBOjrk6ZckI41pSL+4RG0tecrM\nZuVdGjOZ9Ngv9OZXzUrdlf0yZ074e1BSd2m9AAkr9eFhciLzMxNdoL4+XqUeFUkWSm2weDH515TU\nk1LqlACnp8n3UQqlNO2Ty5VXqQNkv4rESBqVOj03Kk2pm3CCC/tl7lw9q7a5mSxm47JICiRM6mfP\nxnfx19XNHqUel/0CiL29BQvE20zSUweKLZgo26Rpn1On0kPqMqVuuvJRnKg0pV5XR8SAibXhwn5Z\nuhTo7Q3/vZYWcv1UtFKPk9TpBW9yh9Xt/eICLtIvs1mpA8UWTNRtsrNoy03qoggdq9S9/WKH+nrz\nfefCfgH0Fkuh52/FK/W4CNTmUStJpX7ppaRVry1MIo02UCn1NHjqQHECxgWpHz5MSLOtzc34bCBb\nci1tDb0AQlS1tZVlv5iSugv7RRdVQ+px2i9Aekl9+XLg3nvt/z6JQikgVq0dHemxX55/Hnj0UeDZ\nZ6OT+oEDZh0a44CO/ZIWpU6XN5wNSj2JXvC0dYhr+yXR9MvAQLz2C/uvDhobSX48ibtyVJg09LLB\nBRcA73ufeF/o2C+uT0wR3vxmcmO88EKyXNutt9q/lyyXnzQaGkhfoUoolALkJljNSt2V/aK7LcC9\nUk+U1M+eDQ/k28KG1OvrgVdfLa9S0wXt0Tw9HU96qKmJrLcqwsqVpUvAAcnbLz/8obv3WrwY+PnP\ny+unAwXScbXwdNx4z3uIlVgJqK83V9xJKnV6zVS0Uj97Nr51C21IHRBPf08jGhvJdP36+uRvQm9/\nO/nikbT94hKLF5NeN2vXlnccsvO2rk6+gEY58fd/X+4R6CPtnvqcOcTOcl3TqSpPnWaZqxFUqafp\nAk86/eISixeXP/kCFEhHVCjNZsWLk3jooa7Ozn4R1TjiwOLFwK9/7f59E28TEGf6JU2E5xr0ETwt\nRTOg8kkdSA+pi/qps697mMPWU6d/mwSWL3f/nomSOhBvobSaSZ3OxE3TRZ60p+4SaSN1/tytqSFf\nafLTKw229gtQGeEJGaqG1OvqqpvUAXKCpukzVrqnDpQ//aKqBdXXe1KPAttCKeBJXQteqUdHY2P6\nlHqlknprK8kJp0Wpi6ay23jCHgWsWAGsW2f2N0nbL3Ggqki9ku+uOmhsTNfJVsn2C0DUehpIXZZo\n8ko9Gi67zDyt4+0XA3j7JTrSptQr2X4BSEzzkkvKO4aGBjmBeKWePKrBfkk0pw54+yUK0kbqlWy/\nAMCXv1zuEajPW6/Uk0c12C+Jk7qPNNojjfbL5CSZ5VqJpJ4GqIrfntSTRzXYL16pVxDSqtTzeU/q\ntvD2S7pQDUrde+oVhDRGGqenC7N5PczhlXq6UA2eemKkThWHV+r2SJtSz2QImXuVbg/VeeuVevKo\nBvslMVLPZMgO86Ruj7SROkD2uSd1e6jsF6/Uk4e3XwwRJ6nPBvslbYVSgOx3T+r28PZLulAN9kui\nQ/dKPRrSSOpeqUeDL5SmC9VgvyRO6j7SaI/GxvQVJD2pR4PPqacL1WC/JErqH/gA8Ed/FM9719VV\n9t1VBw0NntSrDSr7pa7Ok3rSoP3rK5lLEh36Jz8Z33vPFqUuavxUTnhPPRrCCqXefkkWNNDhST0F\nmC2knjZ4pR4NXqmnD83Nlc0lntQrCI2NQBCUexTF8KQeDWGeulfqyaO52Sv1VGA2kHpzM5nBmSZ4\n+yUafE49fXjoofhqf0mgakj9Pe8BNmwo9yjixYc/7JV6tWHBArKivAjefikPbrml3COIhqoh9cWL\nC0uUVSsWLSr3CErhST0abriBfIng7RcPG1QNqXuUB57Uo0G04hHFDTcAf/zHyY3FozrgSd0jEryn\nHh/+4i/KPQKPSkTKUs8elQav1D080oVQUt++fTtWr16NjRs3oq+vr+i173znO7j88suxfv16PPro\no7ENslqwe/fucg/BOWxJvRr3hS38vijA74voUJJ6b28v9u3bhxdeeAE7duzAHXfcMfNaLpfDxz72\nMezZswff+973cNddd2FwcDD2AVcyqvGEtbVfqnFf2MLviwL8vogOJanv2bMHXV1dAIDOzk709vYi\nm80CAPr6+rBs2TK0t7ejvb0dK1aswN69e+MfsUeq0NoKzJ1b7lF4eHhQKAulIyMjWLJkCQCgqakJ\nHR0dGB0dRVNTE4aHh3HeeefN/O7SpUsxOjoa72g9UocHHwTmzCn3KDw8PGYQKPDFL34xuOeee4Ig\nCIKJiYmgra1t5rW+vr5g3bp1M9+vW7cu2LdvX8l7APBf/st/+S//ZfFlA6VSX7t2LXp6egAQK2bN\nmjUzr1188cU4ceIEBgcHMT09jSNHjmDlypUl7xGkbQqkh4eHRxVDSeqrVq3Cli1b0NXVhVwuh127\ndqGnpwebN2/Gpk2b8OCDD+Jd73oXTp8+jfvvvx9z/HO4h4eHR3lhpe818fGPfzx485vfHGzYsCH4\nzW9+E+emUofp6eng9ttvD6666qrg6quvDp599tlg//79werVq4N169YFd999d7mHmDgOHjwYtLa2\nBt/4xjdm9b747ne/G9x8883BlVdeGfT09MzafTE2Nhb8+Z//ebB69epg5cqVwcMPPzyr9kVfX1+w\nZs2aYMOGDUEQBNLPbsqjsZH6z3/+82D9+vVBEATB008/HaxZsyauTaUSP/jBD4Ibb7wxCIIgeOaZ\nZ4K3ve1twY033hj87Gc/C4IgCK6//vrgRz/6UTmHmCjy+Xzwzne+M+js7Ay+8Y1vBFu2bJmV++LY\nsWPB+vXrg+np6WBkZCTYvn17sH79+lm5L77yla8EN910UxAEQXD06NGgpaVlVu2LG2+8MdixY0ew\ncePGIAgC4TVhw6OxzShVxSFnA7Zs2YLvf//7AICDBw8CAJ5//nmsXbsWALBmzRo89dRT5Rpe4vjm\nN7+JFStW4I1vfCMAYN++fbNyXzz++OPo6OjA+9//fmzYsAGdnZ146aWXZuW+WLZsGYaGhpDNZnHs\n2DGcd955ePHFF2fNvnjsscdw/fXXz9QdRdfEc889h+uuuw6APo/GRuqyOORsw/Hjx/HJT34Sn/70\np9HY2Ijac4uMLl26FCMjI2UeXTI4ffo0PvWpT+ETn/jEzM9m67547bXX8NJLL+GrX/0qvv3tb2Pr\n1q0YHx+flfti06ZNuPLKK7F8+XKsX78ejzzyCJqammbNvqipqSkKkoiuCTY6rsujsZF6R0cH+vv7\nAZDZp2NjY+jo6Ihrc6nE2bNncdNNN+Gee+7BunXrEAQBJicnAQD9/f1Yvnx5mUeYDP7mb/4G27Zt\nQ3t7OwCSiJqt+6K1tRXXX389WlpasHTpUvzRudUYZuO++PKXv4xjx47htddewzPPPINbzjUyn437\nAkDJNbFs2TIrHo2N1NeuXYs9e/YAKI1DzgacOXMGmzZtwkc+8hF84AMfAEAeqfbu3YsgCPDss8/O\nPGpVOwYHB/Hwww9j/fr1+NGPfoRPfepTmJiYmJX7gl4X09PTOHnyJE6cOIHNmzfPyn3x6quvYtmy\nZWhoaMAFF1yA8fHxWXuNAKX8cO2119rxqGPvvwj/+I//GFx33XXBmjVrggMHDsS5qdThvvvuCxYu\nXBh0d3cH3d3dwfve977g8OHDwTve8Y5g9erVwY4dO8o9xLLggx/8YPDII4/M6n1x9913B1dddVXw\npje9KXjsscdm7b44duxYsHnz5mDt2rXBW9/61uDhhx+edfti9+7dM4VS2Wc35dFMEPjZQR4eHh7V\nAt9P3cPDw6OK4Endw8PDo4rgSd3Dw8OjiuBJ3cPDw6OK4Endw8PDo4rgSd3Dw8OjiuBJ3cPDw6OK\n8P8BdhDzr6dIpNsAAAAASUVORK5CYII=\n" | |
|
59 | } | |
|
60 | ], | |
|
61 | "collapsed": false, | |
|
62 | "prompt_number": 18, | |
|
63 | "input": "for i in range(10):\n figure()\n plot(rand(100))\n clear_output()\n show()\n time.sleep(0.25)\n" | |
|
64 | }, | |
|
65 | { | |
|
66 | "source": "And we can even selectively clear only a subset of stdout/stderr/other display such as figures\n(all are cleared by default).", | |
|
67 | "cell_type": "markdown" | |
|
68 | }, | |
|
69 | { | |
|
70 | "cell_type": "code", | |
|
71 | "language": "python", | |
|
72 | "outputs": [ | |
|
73 | { | |
|
74 | "output_type": "stream", | |
|
75 | "stream": "stdout", | |
|
76 | "text": "Time step: 0\n" | |
|
77 | }, | |
|
78 | { | |
|
79 | "output_type": "stream", | |
|
80 | "stream": "stdout", | |
|
81 | "text": "Time step: 1\n" | |
|
82 | }, | |
|
83 | { | |
|
84 | "output_type": "stream", | |
|
85 | "stream": "stdout", | |
|
86 | "text": "Time step: 2\n" | |
|
87 | }, | |
|
88 | { | |
|
89 | "output_type": "stream", | |
|
90 | "stream": "stdout", | |
|
91 | "text": "Time step: 3\n" | |
|
92 | }, | |
|
93 | { | |
|
94 | "output_type": "display_data", | |
|
95 | "png": "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\nZjzrn0OUXq0AAAAASUVORK5CYII=\n" | |
|
96 | } | |
|
97 | ], | |
|
98 | "collapsed": false, | |
|
99 | "prompt_number": 19, | |
|
100 | "input": "for i in range(4):\n print \"Time step: %i\" % i\n figure()\n plot(rand(100))\n # clear plots, but not stdout:\n clear_output(stdout=False)\n show()\n time.sleep(0.25)\n" | |
|
101 | } | |
|
102 | ] | |
|
103 | } | |
|
104 | ], | |
|
105 | "metadata": { | |
|
106 | "name": "clear_output" | |
|
107 | }, | |
|
108 | "nbformat": 2 | |
|
109 | } No newline at end of file |
@@ -1,383 +1,407 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | """Top-level display functions for displaying object in different formats. |
|
3 | 3 | |
|
4 | 4 | Authors: |
|
5 | 5 | |
|
6 | 6 | * Brian Granger |
|
7 | 7 | """ |
|
8 | 8 | |
|
9 | 9 | #----------------------------------------------------------------------------- |
|
10 | 10 | # Copyright (C) 2008-2010 The IPython Development Team |
|
11 | 11 | # |
|
12 | 12 | # Distributed under the terms of the BSD License. The full license is in |
|
13 | 13 | # the file COPYING, distributed as part of this software. |
|
14 | 14 | #----------------------------------------------------------------------------- |
|
15 | 15 | |
|
16 | 16 | #----------------------------------------------------------------------------- |
|
17 | 17 | # Imports |
|
18 | 18 | #----------------------------------------------------------------------------- |
|
19 | 19 | |
|
20 | 20 | from .displaypub import ( |
|
21 | 21 | publish_pretty, publish_html, |
|
22 | 22 | publish_latex, publish_svg, |
|
23 | 23 | publish_png, publish_json, |
|
24 | 24 | publish_javascript, publish_jpeg |
|
25 | 25 | ) |
|
26 | 26 | |
|
27 | 27 | #----------------------------------------------------------------------------- |
|
28 | 28 | # Main functions |
|
29 | 29 | #----------------------------------------------------------------------------- |
|
30 | 30 | |
|
31 | 31 | def display(*objs, **kwargs): |
|
32 | 32 | """Display a Python object in all frontends. |
|
33 | 33 | |
|
34 | 34 | By default all representations will be computed and sent to the frontends. |
|
35 | 35 | Frontends can decide which representation is used and how. |
|
36 | 36 | |
|
37 | 37 | Parameters |
|
38 | 38 | ---------- |
|
39 | 39 | objs : tuple of objects |
|
40 | 40 | The Python objects to display. |
|
41 | 41 | include : list or tuple, optional |
|
42 | 42 | A list of format type strings (MIME types) to include in the |
|
43 | 43 | format data dict. If this is set *only* the format types included |
|
44 | 44 | in this list will be computed. |
|
45 | 45 | exclude : list or tuple, optional |
|
46 | 46 | A list of format type string (MIME types) to exclue in the format |
|
47 | 47 | data dict. If this is set all format types will be computed, |
|
48 | 48 | except for those included in this argument. |
|
49 | 49 | """ |
|
50 | 50 | include = kwargs.get('include') |
|
51 | 51 | exclude = kwargs.get('exclude') |
|
52 | 52 | |
|
53 | 53 | from IPython.core.interactiveshell import InteractiveShell |
|
54 | 54 | inst = InteractiveShell.instance() |
|
55 | 55 | format = inst.display_formatter.format |
|
56 | 56 | publish = inst.display_pub.publish |
|
57 | 57 | |
|
58 | 58 | for obj in objs: |
|
59 | 59 | format_dict = format(obj, include=include, exclude=exclude) |
|
60 | 60 | publish('IPython.core.display.display', format_dict) |
|
61 | 61 | |
|
62 | 62 | |
|
63 | 63 | def display_pretty(*objs, **kwargs): |
|
64 | 64 | """Display the pretty (default) representation of an object. |
|
65 | 65 | |
|
66 | 66 | Parameters |
|
67 | 67 | ---------- |
|
68 | 68 | objs : tuple of objects |
|
69 | 69 | The Python objects to display, or if raw=True raw text data to |
|
70 | 70 | display. |
|
71 | 71 | raw : bool |
|
72 | 72 | Are the data objects raw data or Python objects that need to be |
|
73 | 73 | formatted before display? [default: False] |
|
74 | 74 | """ |
|
75 | 75 | raw = kwargs.pop('raw',False) |
|
76 | 76 | if raw: |
|
77 | 77 | for obj in objs: |
|
78 | 78 | publish_pretty(obj) |
|
79 | 79 | else: |
|
80 | 80 | display(*objs, include=['text/plain']) |
|
81 | 81 | |
|
82 | 82 | |
|
83 | 83 | def display_html(*objs, **kwargs): |
|
84 | 84 | """Display the HTML representation of an object. |
|
85 | 85 | |
|
86 | 86 | Parameters |
|
87 | 87 | ---------- |
|
88 | 88 | objs : tuple of objects |
|
89 | 89 | The Python objects to display, or if raw=True raw HTML data to |
|
90 | 90 | display. |
|
91 | 91 | raw : bool |
|
92 | 92 | Are the data objects raw data or Python objects that need to be |
|
93 | 93 | formatted before display? [default: False] |
|
94 | 94 | """ |
|
95 | 95 | raw = kwargs.pop('raw',False) |
|
96 | 96 | if raw: |
|
97 | 97 | for obj in objs: |
|
98 | 98 | publish_html(obj) |
|
99 | 99 | else: |
|
100 | 100 | display(*objs, include=['text/plain','text/html']) |
|
101 | 101 | |
|
102 | 102 | |
|
103 | 103 | def display_svg(*objs, **kwargs): |
|
104 | 104 | """Display the SVG representation of an object. |
|
105 | 105 | |
|
106 | 106 | Parameters |
|
107 | 107 | ---------- |
|
108 | 108 | objs : tuple of objects |
|
109 | 109 | The Python objects to display, or if raw=True raw svg data to |
|
110 | 110 | display. |
|
111 | 111 | raw : bool |
|
112 | 112 | Are the data objects raw data or Python objects that need to be |
|
113 | 113 | formatted before display? [default: False] |
|
114 | 114 | """ |
|
115 | 115 | raw = kwargs.pop('raw',False) |
|
116 | 116 | if raw: |
|
117 | 117 | for obj in objs: |
|
118 | 118 | publish_svg(obj) |
|
119 | 119 | else: |
|
120 | 120 | display(*objs, include=['text/plain','image/svg+xml']) |
|
121 | 121 | |
|
122 | 122 | |
|
123 | 123 | def display_png(*objs, **kwargs): |
|
124 | 124 | """Display the PNG representation of an object. |
|
125 | 125 | |
|
126 | 126 | Parameters |
|
127 | 127 | ---------- |
|
128 | 128 | objs : tuple of objects |
|
129 | 129 | The Python objects to display, or if raw=True raw png data to |
|
130 | 130 | display. |
|
131 | 131 | raw : bool |
|
132 | 132 | Are the data objects raw data or Python objects that need to be |
|
133 | 133 | formatted before display? [default: False] |
|
134 | 134 | """ |
|
135 | 135 | raw = kwargs.pop('raw',False) |
|
136 | 136 | if raw: |
|
137 | 137 | for obj in objs: |
|
138 | 138 | publish_png(obj) |
|
139 | 139 | else: |
|
140 | 140 | display(*objs, include=['text/plain','image/png']) |
|
141 | 141 | |
|
142 | 142 | |
|
143 | 143 | def display_jpeg(*objs, **kwargs): |
|
144 | 144 | """Display the JPEG representation of an object. |
|
145 | 145 | |
|
146 | 146 | Parameters |
|
147 | 147 | ---------- |
|
148 | 148 | objs : tuple of objects |
|
149 | 149 | The Python objects to display, or if raw=True raw JPEG data to |
|
150 | 150 | display. |
|
151 | 151 | raw : bool |
|
152 | 152 | Are the data objects raw data or Python objects that need to be |
|
153 | 153 | formatted before display? [default: False] |
|
154 | 154 | """ |
|
155 | 155 | raw = kwargs.pop('raw',False) |
|
156 | 156 | if raw: |
|
157 | 157 | for obj in objs: |
|
158 | 158 | publish_jpeg(obj) |
|
159 | 159 | else: |
|
160 | 160 | display(*objs, include=['text/plain','image/jpeg']) |
|
161 | 161 | |
|
162 | 162 | |
|
163 | 163 | def display_latex(*objs, **kwargs): |
|
164 | 164 | """Display the LaTeX representation of an object. |
|
165 | 165 | |
|
166 | 166 | Parameters |
|
167 | 167 | ---------- |
|
168 | 168 | objs : tuple of objects |
|
169 | 169 | The Python objects to display, or if raw=True raw latex data to |
|
170 | 170 | display. |
|
171 | 171 | raw : bool |
|
172 | 172 | Are the data objects raw data or Python objects that need to be |
|
173 | 173 | formatted before display? [default: False] |
|
174 | 174 | """ |
|
175 | 175 | raw = kwargs.pop('raw',False) |
|
176 | 176 | if raw: |
|
177 | 177 | for obj in objs: |
|
178 | 178 | publish_latex(obj) |
|
179 | 179 | else: |
|
180 | 180 | display(*objs, include=['text/plain','text/latex']) |
|
181 | 181 | |
|
182 | 182 | |
|
183 | 183 | def display_json(*objs, **kwargs): |
|
184 | 184 | """Display the JSON representation of an object. |
|
185 | 185 | |
|
186 | 186 | Parameters |
|
187 | 187 | ---------- |
|
188 | 188 | objs : tuple of objects |
|
189 | 189 | The Python objects to display, or if raw=True raw json data to |
|
190 | 190 | display. |
|
191 | 191 | raw : bool |
|
192 | 192 | Are the data objects raw data or Python objects that need to be |
|
193 | 193 | formatted before display? [default: False] |
|
194 | 194 | """ |
|
195 | 195 | raw = kwargs.pop('raw',False) |
|
196 | 196 | if raw: |
|
197 | 197 | for obj in objs: |
|
198 | 198 | publish_json(obj) |
|
199 | 199 | else: |
|
200 | 200 | display(*objs, include=['text/plain','application/json']) |
|
201 | 201 | |
|
202 | 202 | |
|
203 | 203 | def display_javascript(*objs, **kwargs): |
|
204 | 204 | """Display the Javascript representation of an object. |
|
205 | 205 | |
|
206 | 206 | Parameters |
|
207 | 207 | ---------- |
|
208 | 208 | objs : tuple of objects |
|
209 | 209 | The Python objects to display, or if raw=True raw javascript data to |
|
210 | 210 | display. |
|
211 | 211 | raw : bool |
|
212 | 212 | Are the data objects raw data or Python objects that need to be |
|
213 | 213 | formatted before display? [default: False] |
|
214 | 214 | """ |
|
215 | 215 | raw = kwargs.pop('raw',False) |
|
216 | 216 | if raw: |
|
217 | 217 | for obj in objs: |
|
218 | 218 | publish_javascript(obj) |
|
219 | 219 | else: |
|
220 | 220 | display(*objs, include=['text/plain','application/javascript']) |
|
221 | 221 | |
|
222 | 222 | #----------------------------------------------------------------------------- |
|
223 | 223 | # Smart classes |
|
224 | 224 | #----------------------------------------------------------------------------- |
|
225 | 225 | |
|
226 | 226 | |
|
227 | 227 | class DisplayObject(object): |
|
228 | 228 | """An object that wraps data to be displayed.""" |
|
229 | 229 | |
|
230 | 230 | _read_flags = 'r' |
|
231 | 231 | |
|
232 | 232 | def __init__(self, data=None, url=None, filename=None): |
|
233 | 233 | """Create a display object given raw data. |
|
234 | 234 | |
|
235 | 235 | When this object is returned by an expression or passed to the |
|
236 | 236 | display function, it will result in the data being displayed |
|
237 | 237 | in the frontend. The MIME type of the data should match the |
|
238 | 238 | subclasses used, so the Png subclass should be used for 'image/png' |
|
239 | 239 | data. If the data is a URL, the data will first be downloaded |
|
240 | 240 | and then displayed. If |
|
241 | 241 | |
|
242 | 242 | Parameters |
|
243 | 243 | ---------- |
|
244 | 244 | data : unicode, str or bytes |
|
245 | 245 | The raw data or a URL to download the data from. |
|
246 | 246 | url : unicode |
|
247 | 247 | A URL to download the data from. |
|
248 | 248 | filename : unicode |
|
249 | 249 | Path to a local file to load the data from. |
|
250 | 250 | """ |
|
251 | 251 | if data is not None and data.startswith('http'): |
|
252 | 252 | self.url = data |
|
253 | 253 | self.filename = None |
|
254 | 254 | self.data = None |
|
255 | 255 | else: |
|
256 | 256 | self.data = data |
|
257 | 257 | self.url = url |
|
258 | 258 | self.filename = None if filename is None else unicode(filename) |
|
259 | 259 | self.reload() |
|
260 | 260 | |
|
261 | 261 | def reload(self): |
|
262 | 262 | """Reload the raw data from file or URL.""" |
|
263 | 263 | if self.filename is not None: |
|
264 | 264 | with open(self.filename, self._read_flags) as f: |
|
265 | 265 | self.data = f.read() |
|
266 | 266 | elif self.url is not None: |
|
267 | 267 | try: |
|
268 | 268 | import urllib2 |
|
269 | 269 | response = urllib2.urlopen(self.url) |
|
270 | 270 | self.data = response.read() |
|
271 | 271 | # extract encoding from header, if there is one: |
|
272 | 272 | encoding = None |
|
273 | 273 | for sub in response.headers['content-type'].split(';'): |
|
274 | 274 | sub = sub.strip() |
|
275 | 275 | if sub.startswith('charset'): |
|
276 | 276 | encoding = sub.split('=')[-1].strip() |
|
277 | 277 | break |
|
278 | 278 | # decode data, if an encoding was specified |
|
279 | 279 | if encoding: |
|
280 | 280 | self.data = self.data.decode(encoding, 'replace') |
|
281 | 281 | except: |
|
282 | 282 | self.data = None |
|
283 | 283 | |
|
284 | 284 | class Pretty(DisplayObject): |
|
285 | 285 | |
|
286 | 286 | def _repr_pretty_(self): |
|
287 | 287 | return self.data |
|
288 | 288 | |
|
289 | 289 | |
|
290 | 290 | class HTML(DisplayObject): |
|
291 | 291 | |
|
292 | 292 | def _repr_html_(self): |
|
293 | 293 | return self.data |
|
294 | 294 | |
|
295 | 295 | |
|
296 | 296 | class Math(DisplayObject): |
|
297 | 297 | |
|
298 | 298 | def _repr_latex_(self): |
|
299 | 299 | return self.data |
|
300 | 300 | |
|
301 | 301 | |
|
302 | 302 | class SVG(DisplayObject): |
|
303 | 303 | |
|
304 | 304 | def _repr_svg_(self): |
|
305 | 305 | return self.data |
|
306 | 306 | |
|
307 | 307 | |
|
308 | 308 | class JSON(DisplayObject): |
|
309 | 309 | |
|
310 | 310 | def _repr_json_(self): |
|
311 | 311 | return self.data |
|
312 | 312 | |
|
313 | 313 | |
|
314 | 314 | class Javascript(DisplayObject): |
|
315 | 315 | |
|
316 | 316 | def _repr_javascript_(self): |
|
317 | 317 | return self.data |
|
318 | 318 | |
|
319 | 319 | |
|
320 | 320 | class Image(DisplayObject): |
|
321 | 321 | |
|
322 | 322 | _read_flags = 'rb' |
|
323 | 323 | |
|
324 | 324 | def __init__(self, data=None, url=None, filename=None, format=u'png', embed=False): |
|
325 | 325 | """Create a display an PNG/JPEG image given raw data. |
|
326 | 326 | |
|
327 | 327 | When this object is returned by an expression or passed to the |
|
328 | 328 | display function, it will result in the image being displayed |
|
329 | 329 | in the frontend. |
|
330 | 330 | |
|
331 | 331 | Parameters |
|
332 | 332 | ---------- |
|
333 | 333 | data : unicode, str or bytes |
|
334 | 334 | The raw data or a URL to download the data from. |
|
335 | 335 | url : unicode |
|
336 | 336 | A URL to download the data from. |
|
337 | 337 | filename : unicode |
|
338 | 338 | Path to a local file to load the data from. |
|
339 | 339 | format : unicode |
|
340 | 340 | The format of the image data (png/jpeg/jpg). If a filename or URL is given |
|
341 | 341 | for format will be inferred from the filename extension. |
|
342 | 342 | embed : bool |
|
343 | 343 | Should the image data be embedded in the notebook using a data URI (True) |
|
344 | 344 | or be loaded using an <img> tag. Set this to True if you want the image |
|
345 | 345 | to be viewable later with no internet connection. If a filename is given |
|
346 | 346 | embed is always set to True. |
|
347 | 347 | """ |
|
348 | 348 | if filename is not None: |
|
349 | 349 | ext = self._find_ext(filename) |
|
350 | 350 | elif url is not None: |
|
351 | 351 | ext = self._find_ext(url) |
|
352 | 352 | elif data.startswith('http'): |
|
353 | 353 | ext = self._find_ext(data) |
|
354 | 354 | else: |
|
355 | 355 | ext = None |
|
356 | 356 | if ext is not None: |
|
357 | 357 | if ext == u'jpg' or ext == u'jpeg': |
|
358 | 358 | format = u'jpeg' |
|
359 | 359 | if ext == u'png': |
|
360 | 360 | format = u'png' |
|
361 | 361 | self.format = unicode(format).lower() |
|
362 | 362 | self.embed = True if filename is not None else embed |
|
363 | 363 | super(Image, self).__init__(data=data, url=url, filename=filename) |
|
364 | 364 | |
|
365 | 365 | def reload(self): |
|
366 | 366 | """Reload the raw data from file or URL.""" |
|
367 | 367 | if self.embed: |
|
368 | 368 | super(Image,self).reload() |
|
369 | 369 | |
|
370 | 370 | def _repr_html_(self): |
|
371 | 371 | if not self.embed: |
|
372 | 372 | return u'<img src="%s" />' % self.url |
|
373 | 373 | |
|
374 | 374 | def _repr_png_(self): |
|
375 | 375 | if self.embed and self.format == u'png': |
|
376 | 376 | return self.data |
|
377 | 377 | |
|
378 | 378 | def _repr_jpeg_(self): |
|
379 | 379 | if self.embed and (self.format == u'jpeg' or self.format == u'jpg'): |
|
380 | 380 | return self.data |
|
381 | 381 | |
|
382 | 382 | def _find_ext(self, s): |
|
383 | 383 | return unicode(s.split('.')[-1].lower()) |
|
384 | ||
|
385 | ||
|
386 | def clear_output(stdout=True, stderr=True, other=True): | |
|
387 | """Clear the output of the current cell receiving output. | |
|
388 | ||
|
389 | Optionally, each of stdout/stderr or other non-stream data (e.g. anything | |
|
390 | produced by display()) can be excluded from the clear event. | |
|
391 | ||
|
392 | By default, everything is cleared. | |
|
393 | ||
|
394 | Parameters | |
|
395 | ---------- | |
|
396 | stdout : bool [default: True] | |
|
397 | Whether to clear stdout. | |
|
398 | stderr : bool [default: True] | |
|
399 | Whether to clear stderr. | |
|
400 | other : bool [default: True] | |
|
401 | Whether to clear everything else that is not stdout/stderr | |
|
402 | (e.g. figures,images,HTML, any result of display()). | |
|
403 | """ | |
|
404 | from IPython.core.interactiveshell import InteractiveShell | |
|
405 | InteractiveShell.instance().display_pub.clear_output( | |
|
406 | stdout=stdout, stderr=stderr, other=other, | |
|
407 | ) |
@@ -1,298 +1,302 b'' | |||
|
1 | 1 | """An interface for publishing rich data to frontends. |
|
2 | 2 | |
|
3 | 3 | There are two components of the display system: |
|
4 | 4 | |
|
5 | 5 | * Display formatters, which take a Python object and compute the |
|
6 | 6 | representation of the object in various formats (text, HTML, SVg, etc.). |
|
7 | 7 | * The display publisher that is used to send the representation data to the |
|
8 | 8 | various frontends. |
|
9 | 9 | |
|
10 | 10 | This module defines the logic display publishing. The display publisher uses |
|
11 | 11 | the ``display_data`` message type that is defined in the IPython messaging |
|
12 | 12 | spec. |
|
13 | 13 | |
|
14 | 14 | Authors: |
|
15 | 15 | |
|
16 | 16 | * Brian Granger |
|
17 | 17 | """ |
|
18 | 18 | |
|
19 | 19 | #----------------------------------------------------------------------------- |
|
20 | 20 | # Copyright (C) 2008-2010 The IPython Development Team |
|
21 | 21 | # |
|
22 | 22 | # Distributed under the terms of the BSD License. The full license is in |
|
23 | 23 | # the file COPYING, distributed as part of this software. |
|
24 | 24 | #----------------------------------------------------------------------------- |
|
25 | 25 | |
|
26 | 26 | #----------------------------------------------------------------------------- |
|
27 | 27 | # Imports |
|
28 | 28 | #----------------------------------------------------------------------------- |
|
29 | 29 | |
|
30 | 30 | from __future__ import print_function |
|
31 | 31 | |
|
32 | 32 | from IPython.config.configurable import Configurable |
|
33 | 33 | |
|
34 | 34 | #----------------------------------------------------------------------------- |
|
35 | 35 | # Main payload class |
|
36 | 36 | #----------------------------------------------------------------------------- |
|
37 | 37 | |
|
38 | 38 | class DisplayPublisher(Configurable): |
|
39 | 39 | """A traited class that publishes display data to frontends. |
|
40 | 40 | |
|
41 | 41 | Instances of this class are created by the main IPython object and should |
|
42 | 42 | be accessed there. |
|
43 | 43 | """ |
|
44 | 44 | |
|
45 | 45 | def _validate_data(self, source, data, metadata=None): |
|
46 | 46 | """Validate the display data. |
|
47 | 47 | |
|
48 | 48 | Parameters |
|
49 | 49 | ---------- |
|
50 | 50 | source : str |
|
51 | 51 | The fully dotted name of the callable that created the data, like |
|
52 | 52 | :func:`foo.bar.my_formatter`. |
|
53 | 53 | data : dict |
|
54 | 54 | The formata data dictionary. |
|
55 | 55 | metadata : dict |
|
56 | 56 | Any metadata for the data. |
|
57 | 57 | """ |
|
58 | 58 | |
|
59 | 59 | if not isinstance(source, basestring): |
|
60 | 60 | raise TypeError('source must be a str, got: %r' % source) |
|
61 | 61 | if not isinstance(data, dict): |
|
62 | 62 | raise TypeError('data must be a dict, got: %r' % data) |
|
63 | 63 | if metadata is not None: |
|
64 | 64 | if not isinstance(metadata, dict): |
|
65 | 65 | raise TypeError('metadata must be a dict, got: %r' % data) |
|
66 | 66 | |
|
67 | 67 | def publish(self, source, data, metadata=None): |
|
68 | 68 | """Publish data and metadata to all frontends. |
|
69 | 69 | |
|
70 | 70 | See the ``display_data`` message in the messaging documentation for |
|
71 | 71 | more details about this message type. |
|
72 | 72 | |
|
73 | 73 | The following MIME types are currently implemented: |
|
74 | 74 | |
|
75 | 75 | * text/plain |
|
76 | 76 | * text/html |
|
77 | 77 | * text/latex |
|
78 | 78 | * application/json |
|
79 | 79 | * application/javascript |
|
80 | 80 | * image/png |
|
81 | 81 | * image/jpeg |
|
82 | 82 | * image/svg+xml |
|
83 | 83 | |
|
84 | 84 | Parameters |
|
85 | 85 | ---------- |
|
86 | 86 | source : str |
|
87 | 87 | A string that give the function or method that created the data, |
|
88 | 88 | such as 'IPython.core.page'. |
|
89 | 89 | data : dict |
|
90 | 90 | A dictionary having keys that are valid MIME types (like |
|
91 | 91 | 'text/plain' or 'image/svg+xml') and values that are the data for |
|
92 | 92 | that MIME type. The data itself must be a JSON'able data |
|
93 | 93 | structure. Minimally all data should have the 'text/plain' data, |
|
94 | 94 | which can be displayed by all frontends. If more than the plain |
|
95 | 95 | text is given, it is up to the frontend to decide which |
|
96 | 96 | representation to use. |
|
97 | 97 | metadata : dict |
|
98 | 98 | A dictionary for metadata related to the data. This can contain |
|
99 | 99 | arbitrary key, value pairs that frontends can use to interpret |
|
100 | 100 | the data. |
|
101 | 101 | """ |
|
102 | 102 | from IPython.utils import io |
|
103 | 103 | # The default is to simply write the plain text data using io.stdout. |
|
104 | 104 | if data.has_key('text/plain'): |
|
105 | 105 | print(data['text/plain'], file=io.stdout) |
|
106 | 106 | |
|
107 | def clear_output(self, stdout=True, stderr=True, other=True): | |
|
108 | """Clear the output of the cell receiving output.""" | |
|
109 | pass | |
|
110 | ||
|
107 | 111 | |
|
108 | 112 | def publish_display_data(source, data, metadata=None): |
|
109 | 113 | """Publish data and metadata to all frontends. |
|
110 | 114 | |
|
111 | 115 | See the ``display_data`` message in the messaging documentation for |
|
112 | 116 | more details about this message type. |
|
113 | 117 | |
|
114 | 118 | The following MIME types are currently implemented: |
|
115 | 119 | |
|
116 | 120 | * text/plain |
|
117 | 121 | * text/html |
|
118 | 122 | * text/latex |
|
119 | 123 | * application/json |
|
120 | 124 | * application/javascript |
|
121 | 125 | * image/png |
|
122 | 126 | * image/jpeg |
|
123 | 127 | * image/svg+xml |
|
124 | 128 | |
|
125 | 129 | Parameters |
|
126 | 130 | ---------- |
|
127 | 131 | source : str |
|
128 | 132 | A string that give the function or method that created the data, |
|
129 | 133 | such as 'IPython.core.page'. |
|
130 | 134 | data : dict |
|
131 | 135 | A dictionary having keys that are valid MIME types (like |
|
132 | 136 | 'text/plain' or 'image/svg+xml') and values that are the data for |
|
133 | 137 | that MIME type. The data itself must be a JSON'able data |
|
134 | 138 | structure. Minimally all data should have the 'text/plain' data, |
|
135 | 139 | which can be displayed by all frontends. If more than the plain |
|
136 | 140 | text is given, it is up to the frontend to decide which |
|
137 | 141 | representation to use. |
|
138 | 142 | metadata : dict |
|
139 | 143 | A dictionary for metadata related to the data. This can contain |
|
140 | 144 | arbitrary key, value pairs that frontends can use to interpret |
|
141 | 145 | the data. |
|
142 | 146 | """ |
|
143 | 147 | from IPython.core.interactiveshell import InteractiveShell |
|
144 | 148 | InteractiveShell.instance().display_pub.publish( |
|
145 | 149 | source, |
|
146 | 150 | data, |
|
147 | 151 | metadata |
|
148 | 152 | ) |
|
149 | 153 | |
|
150 | 154 | |
|
151 | 155 | def publish_pretty(data, metadata=None): |
|
152 | 156 | """Publish raw text data to all frontends. |
|
153 | 157 | |
|
154 | 158 | Parameters |
|
155 | 159 | ---------- |
|
156 | 160 | data : unicode |
|
157 | 161 | The raw text data to publish. |
|
158 | 162 | metadata : dict |
|
159 | 163 | A dictionary for metadata related to the data. This can contain |
|
160 | 164 | arbitrary key, value pairs that frontends can use to interpret |
|
161 | 165 | the data. |
|
162 | 166 | """ |
|
163 | 167 | publish_display_data( |
|
164 | 168 | u'IPython.core.displaypub.publish_pretty', |
|
165 | 169 | {'text/plain':data}, |
|
166 | 170 | metadata=metadata |
|
167 | 171 | ) |
|
168 | 172 | |
|
169 | 173 | |
|
170 | 174 | def publish_html(data, metadata=None): |
|
171 | 175 | """Publish raw HTML data to all frontends. |
|
172 | 176 | |
|
173 | 177 | Parameters |
|
174 | 178 | ---------- |
|
175 | 179 | data : unicode |
|
176 | 180 | The raw HTML data to publish. |
|
177 | 181 | metadata : dict |
|
178 | 182 | A dictionary for metadata related to the data. This can contain |
|
179 | 183 | arbitrary key, value pairs that frontends can use to interpret |
|
180 | 184 | the data. |
|
181 | 185 | """ |
|
182 | 186 | publish_display_data( |
|
183 | 187 | u'IPython.core.displaypub.publish_html', |
|
184 | 188 | {'text/html':data}, |
|
185 | 189 | metadata=metadata |
|
186 | 190 | ) |
|
187 | 191 | |
|
188 | 192 | |
|
189 | 193 | def publish_latex(data, metadata=None): |
|
190 | 194 | """Publish raw LaTeX data to all frontends. |
|
191 | 195 | |
|
192 | 196 | Parameters |
|
193 | 197 | ---------- |
|
194 | 198 | data : unicode |
|
195 | 199 | The raw LaTeX data to publish. |
|
196 | 200 | metadata : dict |
|
197 | 201 | A dictionary for metadata related to the data. This can contain |
|
198 | 202 | arbitrary key, value pairs that frontends can use to interpret |
|
199 | 203 | the data. |
|
200 | 204 | """ |
|
201 | 205 | publish_display_data( |
|
202 | 206 | u'IPython.core.displaypub.publish_latex', |
|
203 | 207 | {'text/latex':data}, |
|
204 | 208 | metadata=metadata |
|
205 | 209 | ) |
|
206 | 210 | |
|
207 | 211 | def publish_png(data, metadata=None): |
|
208 | 212 | """Publish raw binary PNG data to all frontends. |
|
209 | 213 | |
|
210 | 214 | Parameters |
|
211 | 215 | ---------- |
|
212 | 216 | data : str/bytes |
|
213 | 217 | The raw binary PNG data to publish. |
|
214 | 218 | metadata : dict |
|
215 | 219 | A dictionary for metadata related to the data. This can contain |
|
216 | 220 | arbitrary key, value pairs that frontends can use to interpret |
|
217 | 221 | the data. |
|
218 | 222 | """ |
|
219 | 223 | publish_display_data( |
|
220 | 224 | u'IPython.core.displaypub.publish_png', |
|
221 | 225 | {'image/png':data}, |
|
222 | 226 | metadata=metadata |
|
223 | 227 | ) |
|
224 | 228 | |
|
225 | 229 | |
|
226 | 230 | def publish_jpeg(data, metadata=None): |
|
227 | 231 | """Publish raw binary JPEG data to all frontends. |
|
228 | 232 | |
|
229 | 233 | Parameters |
|
230 | 234 | ---------- |
|
231 | 235 | data : str/bytes |
|
232 | 236 | The raw binary JPEG data to publish. |
|
233 | 237 | metadata : dict |
|
234 | 238 | A dictionary for metadata related to the data. This can contain |
|
235 | 239 | arbitrary key, value pairs that frontends can use to interpret |
|
236 | 240 | the data. |
|
237 | 241 | """ |
|
238 | 242 | publish_display_data( |
|
239 | 243 | u'IPython.core.displaypub.publish_jpeg', |
|
240 | 244 | {'image/jpeg':data}, |
|
241 | 245 | metadata=metadata |
|
242 | 246 | ) |
|
243 | 247 | |
|
244 | 248 | |
|
245 | 249 | def publish_svg(data, metadata=None): |
|
246 | 250 | """Publish raw SVG data to all frontends. |
|
247 | 251 | |
|
248 | 252 | Parameters |
|
249 | 253 | ---------- |
|
250 | 254 | data : unicode |
|
251 | 255 | The raw SVG data to publish. |
|
252 | 256 | metadata : dict |
|
253 | 257 | A dictionary for metadata related to the data. This can contain |
|
254 | 258 | arbitrary key, value pairs that frontends can use to interpret |
|
255 | 259 | the data. |
|
256 | 260 | """ |
|
257 | 261 | publish_display_data( |
|
258 | 262 | u'IPython.core.displaypub.publish_svg', |
|
259 | 263 | {'image/svg+xml':data}, |
|
260 | 264 | metadata=metadata |
|
261 | 265 | ) |
|
262 | 266 | |
|
263 | 267 | def publish_json(data, metadata=None): |
|
264 | 268 | """Publish raw JSON data to all frontends. |
|
265 | 269 | |
|
266 | 270 | Parameters |
|
267 | 271 | ---------- |
|
268 | 272 | data : unicode |
|
269 | 273 | The raw JSON data to publish. |
|
270 | 274 | metadata : dict |
|
271 | 275 | A dictionary for metadata related to the data. This can contain |
|
272 | 276 | arbitrary key, value pairs that frontends can use to interpret |
|
273 | 277 | the data. |
|
274 | 278 | """ |
|
275 | 279 | publish_display_data( |
|
276 | 280 | u'IPython.core.displaypub.publish_json', |
|
277 | 281 | {'application/json':data}, |
|
278 | 282 | metadata=metadata |
|
279 | 283 | ) |
|
280 | 284 | |
|
281 | 285 | def publish_javascript(data, metadata=None): |
|
282 | 286 | """Publish raw Javascript data to all frontends. |
|
283 | 287 | |
|
284 | 288 | Parameters |
|
285 | 289 | ---------- |
|
286 | 290 | data : unicode |
|
287 | 291 | The raw Javascript data to publish. |
|
288 | 292 | metadata : dict |
|
289 | 293 | A dictionary for metadata related to the data. This can contain |
|
290 | 294 | arbitrary key, value pairs that frontends can use to interpret |
|
291 | 295 | the data. |
|
292 | 296 | """ |
|
293 | 297 | publish_display_data( |
|
294 | 298 | u'IPython.core.displaypub.publish_javascript', |
|
295 | 299 | {'application/javascript':data}, |
|
296 | 300 | metadata=metadata |
|
297 | 301 | ) |
|
298 | 302 |
@@ -1,486 +1,518 b'' | |||
|
1 | 1 | //---------------------------------------------------------------------------- |
|
2 | 2 | // Copyright (C) 2008-2011 The IPython Development Team |
|
3 | 3 | // |
|
4 | 4 | // Distributed under the terms of the BSD License. The full license is in |
|
5 | 5 | // the file COPYING, distributed as part of this software. |
|
6 | 6 | //---------------------------------------------------------------------------- |
|
7 | 7 | |
|
8 | 8 | //============================================================================ |
|
9 | 9 | // CodeCell |
|
10 | 10 | //============================================================================ |
|
11 | 11 | |
|
12 | 12 | var IPython = (function (IPython) { |
|
13 | 13 | |
|
14 | 14 | var utils = IPython.utils; |
|
15 | 15 | |
|
16 | 16 | var CodeCell = function (notebook) { |
|
17 | 17 | this.code_mirror = null; |
|
18 | 18 | this.input_prompt_number = ' '; |
|
19 | 19 | this.is_completing = false; |
|
20 | 20 | this.completion_cursor = null; |
|
21 | 21 | this.outputs = []; |
|
22 | 22 | this.collapsed = false; |
|
23 | 23 | IPython.Cell.apply(this, arguments); |
|
24 | 24 | }; |
|
25 | 25 | |
|
26 | 26 | |
|
27 | 27 | CodeCell.prototype = new IPython.Cell(); |
|
28 | 28 | |
|
29 | 29 | |
|
30 | 30 | CodeCell.prototype.create_element = function () { |
|
31 | 31 | var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox'); |
|
32 | 32 | cell.attr('tabindex','2'); |
|
33 | 33 | var input = $('<div></div>').addClass('input hbox'); |
|
34 | 34 | input.append($('<div/>').addClass('prompt input_prompt')); |
|
35 | 35 | var input_area = $('<div/>').addClass('input_area box-flex1'); |
|
36 | 36 | this.code_mirror = CodeMirror(input_area.get(0), { |
|
37 | 37 | indentUnit : 4, |
|
38 | 38 | mode: 'python', |
|
39 | 39 | theme: 'ipython', |
|
40 | 40 | onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this) |
|
41 | 41 | }); |
|
42 | 42 | input.append(input_area); |
|
43 | 43 | var output = $('<div></div>').addClass('output vbox'); |
|
44 | 44 | cell.append(input).append(output); |
|
45 | 45 | this.element = cell; |
|
46 | 46 | this.collapse() |
|
47 | 47 | }; |
|
48 | 48 | |
|
49 | 49 | |
|
50 | 50 | CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) { |
|
51 | 51 | // This method gets called in CodeMirror's onKeyDown/onKeyPress |
|
52 | 52 | // handlers and is used to provide custom key handling. Its return |
|
53 | 53 | // value is used to determine if CodeMirror should ignore the event: |
|
54 | 54 | // true = ignore, false = don't ignore. |
|
55 | 55 | if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) { |
|
56 | 56 | // Always ignore shift-enter in CodeMirror as we handle it. |
|
57 | 57 | return true; |
|
58 | 58 | } else if (event.keyCode === 9 && event.type == 'keydown') { |
|
59 | 59 | // Tab completion. |
|
60 | 60 | var cur = editor.getCursor(); |
|
61 | 61 | var pre_cursor = editor.getRange({line:cur.line,ch:0},cur).trim(); |
|
62 | 62 | if (pre_cursor === "") { |
|
63 | 63 | // Don't autocomplete if the part of the line before the cursor |
|
64 | 64 | // is empty. In this case, let CodeMirror handle indentation. |
|
65 | 65 | return false; |
|
66 | 66 | } else { |
|
67 | 67 | // Autocomplete the current line. |
|
68 | 68 | event.stop(); |
|
69 | 69 | var line = editor.getLine(cur.line); |
|
70 | 70 | this.is_completing = true; |
|
71 | 71 | this.completion_cursor = cur; |
|
72 | 72 | IPython.notebook.complete_cell(this, line, cur.ch); |
|
73 | 73 | return true; |
|
74 | 74 | } |
|
75 | 75 | } else if (event.keyCode === 8 && event.type == 'keydown') { |
|
76 | 76 | // If backspace and the line ends with 4 spaces, remove them. |
|
77 | 77 | var cur = editor.getCursor(); |
|
78 | 78 | var line = editor.getLine(cur.line); |
|
79 | 79 | var ending = line.slice(-4); |
|
80 | 80 | if (ending === ' ') { |
|
81 | 81 | editor.replaceRange('', |
|
82 | 82 | {line: cur.line, ch: cur.ch-4}, |
|
83 | 83 | {line: cur.line, ch: cur.ch} |
|
84 | 84 | ); |
|
85 | 85 | event.stop(); |
|
86 | 86 | return true; |
|
87 | 87 | } else { |
|
88 | 88 | return false; |
|
89 | 89 | }; |
|
90 | 90 | } else if (event.keyCode === 76 && event.ctrlKey && event.shiftKey |
|
91 | 91 | && event.type == 'keydown') { |
|
92 | 92 | // toggle line numbers with Ctrl-Shift-L |
|
93 | 93 | this.toggle_line_numbers(); |
|
94 | 94 | } |
|
95 | 95 | else { |
|
96 | 96 | // keypress/keyup also trigger on TAB press, and we don't want to |
|
97 | 97 | // use those to disable tab completion. |
|
98 | 98 | if (this.is_completing && event.keyCode !== 9) { |
|
99 | 99 | var ed_cur = editor.getCursor(); |
|
100 | 100 | var cc_cur = this.completion_cursor; |
|
101 | 101 | if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) { |
|
102 | 102 | this.is_completing = false; |
|
103 | 103 | this.completion_cursor = null; |
|
104 | 104 | }; |
|
105 | 105 | }; |
|
106 | 106 | return false; |
|
107 | 107 | }; |
|
108 | 108 | }; |
|
109 | 109 | |
|
110 | 110 | |
|
111 | 111 | CodeCell.prototype.finish_completing = function (matched_text, matches) { |
|
112 | 112 | // console.log("Got matches", matched_text, matches); |
|
113 | 113 | if (!this.is_completing || matches.length === 0) {return;} |
|
114 | 114 | |
|
115 | 115 | var that = this; |
|
116 | 116 | var cur = this.completion_cursor; |
|
117 | 117 | |
|
118 | 118 | var insert = function (selected_text) { |
|
119 | 119 | that.code_mirror.replaceRange( |
|
120 | 120 | selected_text, |
|
121 | 121 | {line: cur.line, ch: (cur.ch-matched_text.length)}, |
|
122 | 122 | {line: cur.line, ch: cur.ch} |
|
123 | 123 | ); |
|
124 | 124 | }; |
|
125 | 125 | |
|
126 | 126 | if (matches.length === 1) { |
|
127 | 127 | insert(matches[0]); |
|
128 | 128 | setTimeout(function(){that.code_mirror.focus();}, 50); |
|
129 | 129 | return; |
|
130 | 130 | }; |
|
131 | 131 | |
|
132 | 132 | var complete = $('<div/>').addClass('completions'); |
|
133 | 133 | var select = $('<select/>').attr('multiple','true'); |
|
134 | 134 | for (var i=0; i<matches.length; ++i) { |
|
135 | 135 | select.append($('<option/>').text(matches[i])); |
|
136 | 136 | } |
|
137 | 137 | select.children().first().attr('selected','true'); |
|
138 | 138 | select.attr('size',Math.min(10,matches.length)); |
|
139 | 139 | var pos = this.code_mirror.cursorCoords(); |
|
140 | 140 | complete.css('left',pos.x+'px'); |
|
141 | 141 | complete.css('top',pos.yBot+'px'); |
|
142 | 142 | complete.append(select); |
|
143 | 143 | |
|
144 | 144 | $('body').append(complete); |
|
145 | 145 | var done = false; |
|
146 | 146 | |
|
147 | 147 | var close = function () { |
|
148 | 148 | if (done) return; |
|
149 | 149 | done = true; |
|
150 | 150 | complete.remove(); |
|
151 | 151 | that.is_completing = false; |
|
152 | 152 | that.completion_cursor = null; |
|
153 | 153 | }; |
|
154 | 154 | |
|
155 | 155 | var pick = function () { |
|
156 | 156 | insert(select.val()[0]); |
|
157 | 157 | close(); |
|
158 | 158 | setTimeout(function(){that.code_mirror.focus();}, 50); |
|
159 | 159 | }; |
|
160 | 160 | |
|
161 | 161 | select.blur(close); |
|
162 | 162 | select.keydown(function (event) { |
|
163 | 163 | var code = event.which; |
|
164 | 164 | if (code === 13 || code === 32) { |
|
165 | 165 | // Pressing SPACE or ENTER will cause a pick |
|
166 | 166 | event.stopPropagation(); |
|
167 | 167 | event.preventDefault(); |
|
168 | 168 | pick(); |
|
169 | 169 | } else if (code === 38 || code === 40) { |
|
170 | 170 | // We don't want the document keydown handler to handle UP/DOWN, |
|
171 | 171 | // but we want the default action. |
|
172 | 172 | event.stopPropagation(); |
|
173 | 173 | } else { |
|
174 | 174 | // All other key presses exit completion. |
|
175 | 175 | event.stopPropagation(); |
|
176 | 176 | event.preventDefault(); |
|
177 | 177 | close(); |
|
178 | 178 | that.code_mirror.focus(); |
|
179 | 179 | } |
|
180 | 180 | }); |
|
181 | 181 | // Double click also causes a pick. |
|
182 | 182 | select.dblclick(pick); |
|
183 | 183 | select.focus(); |
|
184 | 184 | }; |
|
185 | 185 | |
|
186 | 186 | CodeCell.prototype.toggle_line_numbers = function () { |
|
187 | 187 | if (this.code_mirror.getOption('lineNumbers') == false) { |
|
188 | 188 | this.code_mirror.setOption('lineNumbers', true); |
|
189 | 189 | } else { |
|
190 | 190 | this.code_mirror.setOption('lineNumbers', false); |
|
191 | 191 | } |
|
192 | 192 | this.code_mirror.refresh() |
|
193 | 193 | }; |
|
194 | 194 | |
|
195 | 195 | CodeCell.prototype.select = function () { |
|
196 | 196 | IPython.Cell.prototype.select.apply(this); |
|
197 | 197 | // Todo: this dance is needed because as of CodeMirror 2.12, focus is |
|
198 | 198 | // not causing the cursor to blink if the editor is empty initially. |
|
199 | 199 | // While this seems to fix the issue, this should be fixed |
|
200 | 200 | // in CodeMirror proper. |
|
201 | 201 | var s = this.code_mirror.getValue(); |
|
202 | 202 | this.code_mirror.focus(); |
|
203 | 203 | if (s === '') this.code_mirror.setValue(''); |
|
204 | 204 | }; |
|
205 | 205 | |
|
206 | 206 | |
|
207 | 207 | CodeCell.prototype.select_all = function () { |
|
208 | 208 | var start = {line: 0, ch: 0}; |
|
209 | 209 | var nlines = this.code_mirror.lineCount(); |
|
210 | 210 | var last_line = this.code_mirror.getLine(nlines-1); |
|
211 | 211 | var end = {line: nlines-1, ch: last_line.length}; |
|
212 | 212 | this.code_mirror.setSelection(start, end); |
|
213 | 213 | }; |
|
214 | 214 | |
|
215 | 215 | |
|
216 | 216 | CodeCell.prototype.append_output = function (json) { |
|
217 | 217 | this.expand(); |
|
218 | 218 | if (json.output_type === 'pyout') { |
|
219 | 219 | this.append_pyout(json); |
|
220 | 220 | } else if (json.output_type === 'pyerr') { |
|
221 | 221 | this.append_pyerr(json); |
|
222 | 222 | } else if (json.output_type === 'display_data') { |
|
223 | 223 | this.append_display_data(json); |
|
224 | 224 | } else if (json.output_type === 'stream') { |
|
225 | 225 | this.append_stream(json); |
|
226 | 226 | }; |
|
227 | 227 | this.outputs.push(json); |
|
228 | 228 | }; |
|
229 | 229 | |
|
230 | 230 | |
|
231 | 231 | CodeCell.prototype.create_output_area = function () { |
|
232 | 232 | var oa = $("<div/>").addClass("hbox output_area"); |
|
233 | 233 | oa.append($('<div/>').addClass('prompt')); |
|
234 | 234 | return oa; |
|
235 | 235 | }; |
|
236 | 236 | |
|
237 | 237 | |
|
238 | 238 | CodeCell.prototype.append_pyout = function (json) { |
|
239 | 239 | n = json.prompt_number || ' '; |
|
240 | 240 | var toinsert = this.create_output_area(); |
|
241 | 241 | toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:'); |
|
242 | 242 | this.append_mime_type(json, toinsert); |
|
243 | 243 | this.element.find('div.output').append(toinsert); |
|
244 | 244 | // If we just output latex, typeset it. |
|
245 | 245 | if (json.latex !== undefined) { |
|
246 | 246 | MathJax.Hub.Queue(["Typeset",MathJax.Hub]); |
|
247 | 247 | }; |
|
248 | 248 | }; |
|
249 | 249 | |
|
250 | 250 | |
|
251 | 251 | CodeCell.prototype.append_pyerr = function (json) { |
|
252 | 252 | var tb = json.traceback; |
|
253 | 253 | if (tb !== undefined && tb.length > 0) { |
|
254 | 254 | var s = ''; |
|
255 | 255 | var len = tb.length; |
|
256 | 256 | for (var i=0; i<len; i++) { |
|
257 | 257 | s = s + tb[i] + '\n'; |
|
258 | 258 | } |
|
259 | 259 | s = s + '\n'; |
|
260 | 260 | var toinsert = this.create_output_area(); |
|
261 | 261 | this.append_text(s, toinsert); |
|
262 | 262 | this.element.find('div.output').append(toinsert); |
|
263 | 263 | }; |
|
264 | 264 | }; |
|
265 | 265 | |
|
266 | 266 | |
|
267 | 267 | CodeCell.prototype.append_stream = function (json) { |
|
268 | 268 | // temporary fix: if stream undefined (json file written prior to this patch), |
|
269 | 269 | // default to most likely stdout: |
|
270 | 270 | if (json.stream == undefined){ |
|
271 | 271 | json.stream = 'stdout'; |
|
272 | 272 | } |
|
273 | 273 | var subclass = "output_"+json.stream; |
|
274 | 274 | if (this.outputs.length > 0){ |
|
275 | 275 | // have at least one output to consider |
|
276 | 276 | var last = this.outputs[this.outputs.length-1]; |
|
277 | 277 | if (last.output_type == 'stream' && json.stream == last.stream){ |
|
278 | 278 | // latest output was in the same stream, |
|
279 | 279 | // so append directly into its pre tag |
|
280 | 280 | this.element.find('div.'+subclass).last().find('pre').append(json.text); |
|
281 | 281 | return; |
|
282 | 282 | } |
|
283 | 283 | } |
|
284 | 284 | |
|
285 | 285 | // If we got here, attach a new div |
|
286 | 286 | var toinsert = this.create_output_area(); |
|
287 | 287 | this.append_text(json.text, toinsert, "output_stream "+subclass); |
|
288 | 288 | this.element.find('div.output').append(toinsert); |
|
289 | 289 | }; |
|
290 | 290 | |
|
291 | 291 | |
|
292 | 292 | CodeCell.prototype.append_display_data = function (json) { |
|
293 | 293 | var toinsert = this.create_output_area(); |
|
294 | 294 | this.append_mime_type(json, toinsert) |
|
295 | 295 | this.element.find('div.output').append(toinsert); |
|
296 | 296 | // If we just output latex, typeset it. |
|
297 | 297 | if (json.latex !== undefined) { |
|
298 | 298 | MathJax.Hub.Queue(["Typeset",MathJax.Hub]); |
|
299 | 299 | }; |
|
300 | 300 | }; |
|
301 | 301 | |
|
302 | 302 | |
|
303 | 303 | CodeCell.prototype.append_mime_type = function (json, element) { |
|
304 | 304 | if (json.html !== undefined) { |
|
305 | 305 | this.append_html(json.html, element); |
|
306 | 306 | } else if (json.latex !== undefined) { |
|
307 | 307 | this.append_latex(json.latex, element); |
|
308 | 308 | } else if (json.svg !== undefined) { |
|
309 | 309 | this.append_svg(json.svg, element); |
|
310 | 310 | } else if (json.png !== undefined) { |
|
311 | 311 | this.append_png(json.png, element); |
|
312 | 312 | } else if (json.jpeg !== undefined) { |
|
313 | 313 | this.append_jpeg(json.jpeg, element); |
|
314 | 314 | } else if (json.text !== undefined) { |
|
315 | 315 | this.append_text(json.text, element); |
|
316 | 316 | }; |
|
317 | 317 | }; |
|
318 | 318 | |
|
319 | 319 | |
|
320 | 320 | CodeCell.prototype.append_html = function (html, element) { |
|
321 | 321 | var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html"); |
|
322 | 322 | toinsert.append(html); |
|
323 | 323 | element.append(toinsert); |
|
324 | 324 | } |
|
325 | 325 | |
|
326 | 326 | |
|
327 | 327 | CodeCell.prototype.append_text = function (data, element, extra_class) { |
|
328 | 328 | var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text"); |
|
329 | 329 | if (extra_class){ |
|
330 | 330 | toinsert.addClass(extra_class); |
|
331 | 331 | } |
|
332 | 332 | toinsert.append($("<pre/>").html(data)); |
|
333 | 333 | element.append(toinsert); |
|
334 | 334 | }; |
|
335 | 335 | |
|
336 | 336 | |
|
337 | 337 | CodeCell.prototype.append_svg = function (svg, element) { |
|
338 | 338 | var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg"); |
|
339 | 339 | toinsert.append(svg); |
|
340 | 340 | element.append(toinsert); |
|
341 | 341 | }; |
|
342 | 342 | |
|
343 | 343 | |
|
344 | 344 | CodeCell.prototype.append_png = function (png, element) { |
|
345 | 345 | var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png"); |
|
346 | 346 | toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png)); |
|
347 | 347 | element.append(toinsert); |
|
348 | 348 | }; |
|
349 | 349 | |
|
350 | 350 | |
|
351 | 351 | CodeCell.prototype.append_jpeg = function (jpeg, element) { |
|
352 | 352 | var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg"); |
|
353 | 353 | toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg)); |
|
354 | 354 | element.append(toinsert); |
|
355 | 355 | }; |
|
356 | 356 | |
|
357 | 357 | |
|
358 | 358 | CodeCell.prototype.append_latex = function (latex, element) { |
|
359 | 359 | // This method cannot do the typesetting because the latex first has to |
|
360 | 360 | // be on the page. |
|
361 | 361 | var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex"); |
|
362 | 362 | toinsert.append(latex); |
|
363 | 363 | element.append(toinsert); |
|
364 | 364 | } |
|
365 | 365 | |
|
366 | 366 | |
|
367 | CodeCell.prototype.clear_output = function () { | |
|
368 |
this.element.find("div.output") |
|
|
369 | this.outputs = []; | |
|
367 | CodeCell.prototype.clear_output = function (stdout, stderr, other) { | |
|
368 | var output_div = this.element.find("div.output"); | |
|
369 | if (stdout && stderr && other){ | |
|
370 | // clear all, no need for logic | |
|
371 | output_div.html(""); | |
|
372 | this.outputs = []; | |
|
373 | return; | |
|
374 | } | |
|
375 | // remove html output | |
|
376 | // each output_subarea that has an identifying class is in an output_area | |
|
377 | // which is the element to be removed. | |
|
378 | if (stdout){ | |
|
379 | output_div.find("div.output_stdout").parent().remove(); | |
|
380 | } | |
|
381 | if (stderr){ | |
|
382 | output_div.find("div.output_stderr").parent().remove(); | |
|
383 | } | |
|
384 | if (other){ | |
|
385 | output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove(); | |
|
386 | } | |
|
387 | ||
|
388 | // remove cleared outputs from JSON list: | |
|
389 | for (var i = this.outputs.length - 1; i >= 0; i--){ | |
|
390 | var out = this.outputs[i]; | |
|
391 | var output_type = out.output_type; | |
|
392 | if (output_type == "display_data" && other){ | |
|
393 | this.outputs.splice(i,1); | |
|
394 | }else if (output_type == "stream"){ | |
|
395 | if (stdout && out.stream == "stdout"){ | |
|
396 | this.outputs.splice(i,1); | |
|
397 | }else if (stderr && out.stream == "stderr"){ | |
|
398 | this.outputs.splice(i,1); | |
|
399 | } | |
|
400 | } | |
|
401 | } | |
|
370 | 402 | }; |
|
371 | 403 | |
|
372 | 404 | |
|
373 | 405 | CodeCell.prototype.clear_input = function () { |
|
374 | 406 | this.code_mirror.setValue(''); |
|
375 | 407 | }; |
|
376 | 408 | |
|
377 | 409 | |
|
378 | 410 | CodeCell.prototype.collapse = function () { |
|
379 | 411 | if (!this.collapsed) { |
|
380 | 412 | this.element.find('div.output').hide(); |
|
381 | 413 | this.collapsed = true; |
|
382 | 414 | }; |
|
383 | 415 | }; |
|
384 | 416 | |
|
385 | 417 | |
|
386 | 418 | CodeCell.prototype.expand = function () { |
|
387 | 419 | if (this.collapsed) { |
|
388 | 420 | this.element.find('div.output').show(); |
|
389 | 421 | this.collapsed = false; |
|
390 | 422 | }; |
|
391 | 423 | }; |
|
392 | 424 | |
|
393 | 425 | |
|
394 | 426 | CodeCell.prototype.toggle_output = function () { |
|
395 | 427 | if (this.collapsed) { |
|
396 | 428 | this.expand(); |
|
397 | 429 | } else { |
|
398 | 430 | this.collapse(); |
|
399 | 431 | }; |
|
400 | 432 | }; |
|
401 | 433 | |
|
402 | 434 | CodeCell.prototype.set_input_prompt = function (number) { |
|
403 | 435 | var n = number || ' '; |
|
404 | 436 | this.input_prompt_number = n |
|
405 | 437 | this.element.find('div.input_prompt').html('In [' + n + ']:'); |
|
406 | 438 | }; |
|
407 | 439 | |
|
408 | 440 | |
|
409 | 441 | CodeCell.prototype.get_code = function () { |
|
410 | 442 | return this.code_mirror.getValue(); |
|
411 | 443 | }; |
|
412 | 444 | |
|
413 | 445 | |
|
414 | 446 | CodeCell.prototype.set_code = function (code) { |
|
415 | 447 | return this.code_mirror.setValue(code); |
|
416 | 448 | }; |
|
417 | 449 | |
|
418 | 450 | |
|
419 | 451 | CodeCell.prototype.at_top = function () { |
|
420 | 452 | var cursor = this.code_mirror.getCursor(); |
|
421 | 453 | if (cursor.line === 0) { |
|
422 | 454 | return true; |
|
423 | 455 | } else { |
|
424 | 456 | return false; |
|
425 | 457 | } |
|
426 | 458 | }; |
|
427 | 459 | |
|
428 | 460 | |
|
429 | 461 | CodeCell.prototype.at_bottom = function () { |
|
430 | 462 | var cursor = this.code_mirror.getCursor(); |
|
431 | 463 | if (cursor.line === (this.code_mirror.lineCount()-1)) { |
|
432 | 464 | return true; |
|
433 | 465 | } else { |
|
434 | 466 | return false; |
|
435 | 467 | } |
|
436 | 468 | }; |
|
437 | 469 | |
|
438 | 470 | |
|
439 | 471 | CodeCell.prototype.fromJSON = function (data) { |
|
440 | 472 | console.log('Import from JSON:', data); |
|
441 | 473 | if (data.cell_type === 'code') { |
|
442 | 474 | if (data.input !== undefined) { |
|
443 | 475 | this.set_code(data.input); |
|
444 | 476 | } |
|
445 | 477 | if (data.prompt_number !== undefined) { |
|
446 | 478 | this.set_input_prompt(data.prompt_number); |
|
447 | 479 | } else { |
|
448 | 480 | this.set_input_prompt(); |
|
449 | 481 | }; |
|
450 | 482 | var len = data.outputs.length; |
|
451 | 483 | for (var i=0; i<len; i++) { |
|
452 | 484 | this.append_output(data.outputs[i]); |
|
453 | 485 | }; |
|
454 | 486 | if (data.collapsed !== undefined) { |
|
455 | 487 | if (data.collapsed) { |
|
456 | 488 | this.collapse(); |
|
457 | 489 | }; |
|
458 | 490 | }; |
|
459 | 491 | }; |
|
460 | 492 | }; |
|
461 | 493 | |
|
462 | 494 | |
|
463 | 495 | CodeCell.prototype.toJSON = function () { |
|
464 | 496 | var data = {}; |
|
465 | 497 | data.input = this.get_code(); |
|
466 | 498 | data.cell_type = 'code'; |
|
467 | 499 | if (this.input_prompt_number !== ' ') { |
|
468 | 500 | data.prompt_number = this.input_prompt_number |
|
469 | 501 | }; |
|
470 | 502 | var outputs = []; |
|
471 | 503 | var len = this.outputs.length; |
|
472 | 504 | for (var i=0; i<len; i++) { |
|
473 | 505 | outputs[i] = this.outputs[i]; |
|
474 | 506 | }; |
|
475 | 507 | data.outputs = outputs; |
|
476 | 508 | data.language = 'python'; |
|
477 | 509 | data.collapsed = this.collapsed; |
|
478 | 510 | // console.log('Export to JSON:',data); |
|
479 | 511 | return data; |
|
480 | 512 | }; |
|
481 | 513 | |
|
482 | 514 | |
|
483 | 515 | IPython.CodeCell = CodeCell; |
|
484 | 516 | |
|
485 | 517 | return IPython; |
|
486 | 518 | }(IPython)); |
@@ -1,995 +1,997 b'' | |||
|
1 | 1 | //---------------------------------------------------------------------------- |
|
2 | 2 | // Copyright (C) 2008-2011 The IPython Development Team |
|
3 | 3 | // |
|
4 | 4 | // Distributed under the terms of the BSD License. The full license is in |
|
5 | 5 | // the file COPYING, distributed as part of this software. |
|
6 | 6 | //---------------------------------------------------------------------------- |
|
7 | 7 | |
|
8 | 8 | //============================================================================ |
|
9 | 9 | // Notebook |
|
10 | 10 | //============================================================================ |
|
11 | 11 | |
|
12 | 12 | var IPython = (function (IPython) { |
|
13 | 13 | |
|
14 | 14 | var utils = IPython.utils; |
|
15 | 15 | |
|
16 | 16 | var Notebook = function (selector) { |
|
17 | 17 | this.element = $(selector); |
|
18 | 18 | this.element.scroll(); |
|
19 | 19 | this.element.data("notebook", this); |
|
20 | 20 | this.next_prompt_number = 1; |
|
21 | 21 | this.kernel = null; |
|
22 | 22 | this.dirty = false; |
|
23 | 23 | this.msg_cell_map = {}; |
|
24 | 24 | this.metadata = {}; |
|
25 | 25 | this.control_key_active = false; |
|
26 | 26 | this.style(); |
|
27 | 27 | this.create_elements(); |
|
28 | 28 | this.bind_events(); |
|
29 | 29 | }; |
|
30 | 30 | |
|
31 | 31 | |
|
32 | 32 | Notebook.prototype.style = function () { |
|
33 | 33 | $('div#notebook').addClass('border-box-sizing'); |
|
34 | 34 | }; |
|
35 | 35 | |
|
36 | 36 | |
|
37 | 37 | Notebook.prototype.create_elements = function () { |
|
38 | 38 | // We add this end_space div to the end of the notebook div to: |
|
39 | 39 | // i) provide a margin between the last cell and the end of the notebook |
|
40 | 40 | // ii) to prevent the div from scrolling up when the last cell is being |
|
41 | 41 | // edited, but is too low on the page, which browsers will do automatically. |
|
42 | 42 | var that = this; |
|
43 | 43 | var end_space = $('<div class="end_space"></div>').height(150); |
|
44 | 44 | end_space.dblclick(function (e) { |
|
45 | 45 | var ncells = that.ncells(); |
|
46 | 46 | that.insert_code_cell_below(ncells-1); |
|
47 | 47 | }); |
|
48 | 48 | this.element.append(end_space); |
|
49 | 49 | $('div#notebook').addClass('border-box-sizing'); |
|
50 | 50 | }; |
|
51 | 51 | |
|
52 | 52 | |
|
53 | 53 | Notebook.prototype.bind_events = function () { |
|
54 | 54 | var that = this; |
|
55 | 55 | $(document).keydown(function (event) { |
|
56 | 56 | // console.log(event); |
|
57 | 57 | if (event.which === 38) { |
|
58 | 58 | var cell = that.selected_cell(); |
|
59 | 59 | if (cell.at_top()) { |
|
60 | 60 | event.preventDefault(); |
|
61 | 61 | that.select_prev(); |
|
62 | 62 | }; |
|
63 | 63 | } else if (event.which === 40) { |
|
64 | 64 | var cell = that.selected_cell(); |
|
65 | 65 | if (cell.at_bottom()) { |
|
66 | 66 | event.preventDefault(); |
|
67 | 67 | that.select_next(); |
|
68 | 68 | }; |
|
69 | 69 | } else if (event.which === 13 && event.shiftKey) { |
|
70 | 70 | that.execute_selected_cell(); |
|
71 | 71 | return false; |
|
72 | 72 | } else if (event.which === 13 && event.ctrlKey) { |
|
73 | 73 | that.execute_selected_cell({terminal:true}); |
|
74 | 74 | return false; |
|
75 | 75 | } else if (event.which === 77 && event.ctrlKey) { |
|
76 | 76 | that.control_key_active = true; |
|
77 | 77 | return false; |
|
78 | 78 | } else if (event.which === 68 && that.control_key_active) { |
|
79 | 79 | // Delete selected cell = d |
|
80 | 80 | that.delete_cell(); |
|
81 | 81 | that.control_key_active = false; |
|
82 | 82 | return false; |
|
83 | 83 | } else if (event.which === 65 && that.control_key_active) { |
|
84 | 84 | // Insert code cell above selected = a |
|
85 | 85 | that.insert_code_cell_above(); |
|
86 | 86 | that.control_key_active = false; |
|
87 | 87 | return false; |
|
88 | 88 | } else if (event.which === 66 && that.control_key_active) { |
|
89 | 89 | // Insert code cell below selected = b |
|
90 | 90 | that.insert_code_cell_below(); |
|
91 | 91 | that.control_key_active = false; |
|
92 | 92 | return false; |
|
93 | 93 | } else if (event.which === 67 && that.control_key_active) { |
|
94 | 94 | // To code = c |
|
95 | 95 | that.to_code(); |
|
96 | 96 | that.control_key_active = false; |
|
97 | 97 | return false; |
|
98 | 98 | } else if (event.which === 77 && that.control_key_active) { |
|
99 | 99 | // To markdown = m |
|
100 | 100 | that.to_markdown(); |
|
101 | 101 | that.control_key_active = false; |
|
102 | 102 | return false; |
|
103 | 103 | } else if (event.which === 84 && that.control_key_active) { |
|
104 | 104 | // Toggle output = t |
|
105 | 105 | that.toggle_output(); |
|
106 | 106 | that.control_key_active = false; |
|
107 | 107 | return false; |
|
108 | 108 | } else if (event.which === 83 && that.control_key_active) { |
|
109 | 109 | // Save notebook = s |
|
110 | 110 | IPython.save_widget.save_notebook(); |
|
111 | 111 | that.control_key_active = false; |
|
112 | 112 | return false; |
|
113 | 113 | } else if (event.which === 74 && that.control_key_active) { |
|
114 | 114 | // Move cell down = j |
|
115 | 115 | that.move_cell_down(); |
|
116 | 116 | that.control_key_active = false; |
|
117 | 117 | return false; |
|
118 | 118 | } else if (event.which === 75 && that.control_key_active) { |
|
119 | 119 | // Move cell up = k |
|
120 | 120 | that.move_cell_up(); |
|
121 | 121 | that.control_key_active = false; |
|
122 | 122 | return false; |
|
123 | 123 | } else if (event.which === 80 && that.control_key_active) { |
|
124 | 124 | // Select previous = p |
|
125 | 125 | that.select_prev(); |
|
126 | 126 | that.control_key_active = false; |
|
127 | 127 | return false; |
|
128 | 128 | } else if (event.which === 78 && that.control_key_active) { |
|
129 | 129 | // Select next = n |
|
130 | 130 | that.select_next(); |
|
131 | 131 | that.control_key_active = false; |
|
132 | 132 | return false; |
|
133 | 133 | } else if (event.which === 76 && that.control_key_active) { |
|
134 | 134 | // Toggle line numbers = l |
|
135 | 135 | that.cell_toggle_line_numbers(); |
|
136 | 136 | that.control_key_active = false; |
|
137 | 137 | return false; |
|
138 | 138 | } else if (event.which === 73 && that.control_key_active) { |
|
139 | 139 | // Interrupt kernel = i |
|
140 | 140 | IPython.notebook.kernel.interrupt(); |
|
141 | 141 | that.control_key_active = false; |
|
142 | 142 | return false; |
|
143 | 143 | } else if (event.which === 190 && that.control_key_active) { |
|
144 | 144 | // Restart kernel = . # matches qt console |
|
145 | 145 | IPython.notebook.restart_kernel(); |
|
146 | 146 | that.control_key_active = false; |
|
147 | 147 | return false; |
|
148 | 148 | } else if (event.which === 72 && that.control_key_active) { |
|
149 | 149 | // Show keyboard shortcuts = h |
|
150 | 150 | that.toggle_keyboard_shortcuts(); |
|
151 | 151 | that.control_key_active = false; |
|
152 | 152 | return false; |
|
153 | 153 | } else if (that.control_key_active) { |
|
154 | 154 | that.control_key_active = false; |
|
155 | 155 | return true; |
|
156 | 156 | }; |
|
157 | 157 | }); |
|
158 | 158 | |
|
159 | 159 | this.element.bind('collapse_pager', function () { |
|
160 | 160 | var app_height = $('div#main_app').height(); // content height |
|
161 | 161 | var splitter_height = $('div#pager_splitter').outerHeight(true); |
|
162 | 162 | var new_height = app_height - splitter_height; |
|
163 | 163 | that.element.animate({height : new_height + 'px'}, 'fast'); |
|
164 | 164 | }); |
|
165 | 165 | |
|
166 | 166 | this.element.bind('expand_pager', function () { |
|
167 | 167 | var app_height = $('div#main_app').height(); // content height |
|
168 | 168 | var splitter_height = $('div#pager_splitter').outerHeight(true); |
|
169 | 169 | var pager_height = $('div#pager').outerHeight(true); |
|
170 | 170 | var new_height = app_height - pager_height - splitter_height; |
|
171 | 171 | that.element.animate({height : new_height + 'px'}, 'fast'); |
|
172 | 172 | }); |
|
173 | 173 | |
|
174 | 174 | this.element.bind('collapse_left_panel', function () { |
|
175 | 175 | var splitter_width = $('div#left_panel_splitter').outerWidth(true); |
|
176 | 176 | var new_margin = splitter_width; |
|
177 | 177 | $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast'); |
|
178 | 178 | }); |
|
179 | 179 | |
|
180 | 180 | this.element.bind('expand_left_panel', function () { |
|
181 | 181 | var splitter_width = $('div#left_panel_splitter').outerWidth(true); |
|
182 | 182 | var left_panel_width = IPython.left_panel.width; |
|
183 | 183 | var new_margin = splitter_width + left_panel_width; |
|
184 | 184 | $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast'); |
|
185 | 185 | }); |
|
186 | 186 | |
|
187 | 187 | $(window).bind('beforeunload', function () { |
|
188 | 188 | var kill_kernel = $('#kill_kernel').prop('checked'); |
|
189 | 189 | if (kill_kernel) { |
|
190 | 190 | that.kernel.kill(); |
|
191 | 191 | } |
|
192 | 192 | if (that.dirty) { |
|
193 | 193 | return "You have unsaved changes that will be lost if you leave this page."; |
|
194 | 194 | }; |
|
195 | 195 | }); |
|
196 | 196 | }; |
|
197 | 197 | |
|
198 | 198 | |
|
199 | 199 | Notebook.prototype.toggle_keyboard_shortcuts = function () { |
|
200 | 200 | // toggles display of keyboard shortcut dialog |
|
201 | 201 | var that = this; |
|
202 | 202 | if ( this.shortcut_dialog ){ |
|
203 | 203 | // if dialog is already shown, close it |
|
204 | 204 | this.shortcut_dialog.dialog("close"); |
|
205 | 205 | this.shortcut_dialog = null; |
|
206 | 206 | return; |
|
207 | 207 | } |
|
208 | 208 | var dialog = $('<div/>'); |
|
209 | 209 | this.shortcut_dialog = dialog; |
|
210 | 210 | var shortcuts = [ |
|
211 | 211 | {key: 'Shift-Enter', help: 'run cell'}, |
|
212 | 212 | {key: 'Ctrl-Enter', help: 'run cell in-place'}, |
|
213 | 213 | {key: 'Ctrl-m d', help: 'delete cell'}, |
|
214 | 214 | {key: 'Ctrl-m a', help: 'insert cell above'}, |
|
215 | 215 | {key: 'Ctrl-m b', help: 'insert cell below'}, |
|
216 | 216 | {key: 'Ctrl-m t', help: 'toggle output'}, |
|
217 | 217 | {key: 'Ctrl-m l', help: 'toggle line numbers'}, |
|
218 | 218 | {key: 'Ctrl-m s', help: 'save notebook'}, |
|
219 | 219 | {key: 'Ctrl-m j', help: 'move cell down'}, |
|
220 | 220 | {key: 'Ctrl-m k', help: 'move cell up'}, |
|
221 | 221 | {key: 'Ctrl-m c', help: 'code cell'}, |
|
222 | 222 | {key: 'Ctrl-m m', help: 'markdown cell'}, |
|
223 | 223 | {key: 'Ctrl-m p', help: 'select previous'}, |
|
224 | 224 | {key: 'Ctrl-m n', help: 'select next'}, |
|
225 | 225 | {key: 'Ctrl-m i', help: 'interrupt kernel'}, |
|
226 | 226 | {key: 'Ctrl-m .', help: 'restart kernel'}, |
|
227 | 227 | {key: 'Ctrl-m h', help: 'show keyboard shortcuts'} |
|
228 | 228 | ]; |
|
229 | 229 | for (var i=0; i<shortcuts.length; i++) { |
|
230 | 230 | dialog.append($('<div>'). |
|
231 | 231 | append($('<span/>').addClass('shortcut_key').html(shortcuts[i].key)). |
|
232 | 232 | append($('<span/>').addClass('shortcut_descr').html(' : ' + shortcuts[i].help)) |
|
233 | 233 | ); |
|
234 | 234 | }; |
|
235 | 235 | dialog.bind('dialogclose', function(event) { |
|
236 | 236 | // dialog has been closed, allow it to be drawn again. |
|
237 | 237 | that.shortcut_dialog = null; |
|
238 | 238 | }); |
|
239 | 239 | dialog.dialog({title: 'Keyboard shortcuts'}); |
|
240 | 240 | }; |
|
241 | 241 | |
|
242 | 242 | |
|
243 | 243 | Notebook.prototype.scroll_to_bottom = function () { |
|
244 | 244 | this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0); |
|
245 | 245 | }; |
|
246 | 246 | |
|
247 | 247 | |
|
248 | 248 | Notebook.prototype.scroll_to_top = function () { |
|
249 | 249 | this.element.animate({scrollTop:0}, 0); |
|
250 | 250 | }; |
|
251 | 251 | |
|
252 | 252 | |
|
253 | 253 | // Cell indexing, retrieval, etc. |
|
254 | 254 | |
|
255 | 255 | |
|
256 | 256 | Notebook.prototype.cell_elements = function () { |
|
257 | 257 | return this.element.children("div.cell"); |
|
258 | 258 | } |
|
259 | 259 | |
|
260 | 260 | |
|
261 | 261 | Notebook.prototype.ncells = function (cell) { |
|
262 | 262 | return this.cell_elements().length; |
|
263 | 263 | } |
|
264 | 264 | |
|
265 | 265 | |
|
266 | 266 | // TODO: we are often calling cells as cells()[i], which we should optimize |
|
267 | 267 | // to cells(i) or a new method. |
|
268 | 268 | Notebook.prototype.cells = function () { |
|
269 | 269 | return this.cell_elements().toArray().map(function (e) { |
|
270 | 270 | return $(e).data("cell"); |
|
271 | 271 | }); |
|
272 | 272 | } |
|
273 | 273 | |
|
274 | 274 | |
|
275 | 275 | Notebook.prototype.find_cell_index = function (cell) { |
|
276 | 276 | var result = null; |
|
277 | 277 | this.cell_elements().filter(function (index) { |
|
278 | 278 | if ($(this).data("cell") === cell) { |
|
279 | 279 | result = index; |
|
280 | 280 | }; |
|
281 | 281 | }); |
|
282 | 282 | return result; |
|
283 | 283 | }; |
|
284 | 284 | |
|
285 | 285 | |
|
286 | 286 | Notebook.prototype.index_or_selected = function (index) { |
|
287 | 287 | return index || this.selected_index() || 0; |
|
288 | 288 | } |
|
289 | 289 | |
|
290 | 290 | |
|
291 | 291 | Notebook.prototype.select = function (index) { |
|
292 | 292 | if (index !== undefined && index >= 0 && index < this.ncells()) { |
|
293 | 293 | if (this.selected_index() !== null) { |
|
294 | 294 | this.selected_cell().unselect(); |
|
295 | 295 | }; |
|
296 | 296 | this.cells()[index].select(); |
|
297 | 297 | }; |
|
298 | 298 | return this; |
|
299 | 299 | }; |
|
300 | 300 | |
|
301 | 301 | |
|
302 | 302 | Notebook.prototype.select_next = function () { |
|
303 | 303 | var index = this.selected_index(); |
|
304 | 304 | if (index !== null && index >= 0 && (index+1) < this.ncells()) { |
|
305 | 305 | this.select(index+1); |
|
306 | 306 | }; |
|
307 | 307 | return this; |
|
308 | 308 | }; |
|
309 | 309 | |
|
310 | 310 | |
|
311 | 311 | Notebook.prototype.select_prev = function () { |
|
312 | 312 | var index = this.selected_index(); |
|
313 | 313 | if (index !== null && index >= 0 && (index-1) < this.ncells()) { |
|
314 | 314 | this.select(index-1); |
|
315 | 315 | }; |
|
316 | 316 | return this; |
|
317 | 317 | }; |
|
318 | 318 | |
|
319 | 319 | |
|
320 | 320 | Notebook.prototype.selected_index = function () { |
|
321 | 321 | var result = null; |
|
322 | 322 | this.cell_elements().filter(function (index) { |
|
323 | 323 | if ($(this).data("cell").selected === true) { |
|
324 | 324 | result = index; |
|
325 | 325 | }; |
|
326 | 326 | }); |
|
327 | 327 | return result; |
|
328 | 328 | }; |
|
329 | 329 | |
|
330 | 330 | |
|
331 | 331 | Notebook.prototype.cell_for_msg = function (msg_id) { |
|
332 | 332 | var cell_id = this.msg_cell_map[msg_id]; |
|
333 | 333 | var result = null; |
|
334 | 334 | this.cell_elements().filter(function (index) { |
|
335 | 335 | cell = $(this).data("cell"); |
|
336 | 336 | if (cell.cell_id === cell_id) { |
|
337 | 337 | result = cell; |
|
338 | 338 | }; |
|
339 | 339 | }); |
|
340 | 340 | return result; |
|
341 | 341 | }; |
|
342 | 342 | |
|
343 | 343 | |
|
344 | 344 | Notebook.prototype.selected_cell = function () { |
|
345 | 345 | return this.cell_elements().eq(this.selected_index()).data("cell"); |
|
346 | 346 | } |
|
347 | 347 | |
|
348 | 348 | |
|
349 | 349 | // Cell insertion, deletion and moving. |
|
350 | 350 | |
|
351 | 351 | |
|
352 | 352 | Notebook.prototype.delete_cell = function (index) { |
|
353 | 353 | var i = index || this.selected_index(); |
|
354 | 354 | if (i !== null && i >= 0 && i < this.ncells()) { |
|
355 | 355 | this.cell_elements().eq(i).remove(); |
|
356 | 356 | if (i === (this.ncells())) { |
|
357 | 357 | this.select(i-1); |
|
358 | 358 | } else { |
|
359 | 359 | this.select(i); |
|
360 | 360 | }; |
|
361 | 361 | }; |
|
362 | 362 | this.dirty = true; |
|
363 | 363 | return this; |
|
364 | 364 | }; |
|
365 | 365 | |
|
366 | 366 | |
|
367 | 367 | Notebook.prototype.append_cell = function (cell) { |
|
368 | 368 | this.element.find('div.end_space').before(cell.element); |
|
369 | 369 | this.dirty = true; |
|
370 | 370 | return this; |
|
371 | 371 | }; |
|
372 | 372 | |
|
373 | 373 | |
|
374 | 374 | Notebook.prototype.insert_cell_below = function (cell, index) { |
|
375 | 375 | var ncells = this.ncells(); |
|
376 | 376 | if (ncells === 0) { |
|
377 | 377 | this.append_cell(cell); |
|
378 | 378 | return this; |
|
379 | 379 | }; |
|
380 | 380 | if (index >= 0 && index < ncells) { |
|
381 | 381 | this.cell_elements().eq(index).after(cell.element); |
|
382 | 382 | }; |
|
383 | 383 | this.dirty = true; |
|
384 | 384 | return this |
|
385 | 385 | }; |
|
386 | 386 | |
|
387 | 387 | |
|
388 | 388 | Notebook.prototype.insert_cell_above = function (cell, index) { |
|
389 | 389 | var ncells = this.ncells(); |
|
390 | 390 | if (ncells === 0) { |
|
391 | 391 | this.append_cell(cell); |
|
392 | 392 | return this; |
|
393 | 393 | }; |
|
394 | 394 | if (index >= 0 && index < ncells) { |
|
395 | 395 | this.cell_elements().eq(index).before(cell.element); |
|
396 | 396 | }; |
|
397 | 397 | this.dirty = true; |
|
398 | 398 | return this; |
|
399 | 399 | }; |
|
400 | 400 | |
|
401 | 401 | |
|
402 | 402 | Notebook.prototype.move_cell_up = function (index) { |
|
403 | 403 | var i = index || this.selected_index(); |
|
404 | 404 | if (i !== null && i < this.ncells() && i > 0) { |
|
405 | 405 | var pivot = this.cell_elements().eq(i-1); |
|
406 | 406 | var tomove = this.cell_elements().eq(i); |
|
407 | 407 | if (pivot !== null && tomove !== null) { |
|
408 | 408 | tomove.detach(); |
|
409 | 409 | pivot.before(tomove); |
|
410 | 410 | this.select(i-1); |
|
411 | 411 | }; |
|
412 | 412 | }; |
|
413 | 413 | this.dirty = true; |
|
414 | 414 | return this; |
|
415 | 415 | } |
|
416 | 416 | |
|
417 | 417 | |
|
418 | 418 | Notebook.prototype.move_cell_down = function (index) { |
|
419 | 419 | var i = index || this.selected_index(); |
|
420 | 420 | if (i !== null && i < (this.ncells()-1) && i >= 0) { |
|
421 | 421 | var pivot = this.cell_elements().eq(i+1) |
|
422 | 422 | var tomove = this.cell_elements().eq(i) |
|
423 | 423 | if (pivot !== null && tomove !== null) { |
|
424 | 424 | tomove.detach(); |
|
425 | 425 | pivot.after(tomove); |
|
426 | 426 | this.select(i+1); |
|
427 | 427 | }; |
|
428 | 428 | }; |
|
429 | 429 | this.dirty = true; |
|
430 | 430 | return this; |
|
431 | 431 | } |
|
432 | 432 | |
|
433 | 433 | |
|
434 | 434 | Notebook.prototype.sort_cells = function () { |
|
435 | 435 | var ncells = this.ncells(); |
|
436 | 436 | var sindex = this.selected_index(); |
|
437 | 437 | var swapped; |
|
438 | 438 | do { |
|
439 | 439 | swapped = false |
|
440 | 440 | for (var i=1; i<ncells; i++) { |
|
441 | 441 | current = this.cell_elements().eq(i).data("cell"); |
|
442 | 442 | previous = this.cell_elements().eq(i-1).data("cell"); |
|
443 | 443 | if (previous.input_prompt_number > current.input_prompt_number) { |
|
444 | 444 | this.move_cell_up(i); |
|
445 | 445 | swapped = true; |
|
446 | 446 | }; |
|
447 | 447 | }; |
|
448 | 448 | } while (swapped); |
|
449 | 449 | this.select(sindex); |
|
450 | 450 | return this; |
|
451 | 451 | }; |
|
452 | 452 | |
|
453 | 453 | |
|
454 | 454 | Notebook.prototype.insert_code_cell_above = function (index) { |
|
455 | 455 | // TODO: Bounds check for i |
|
456 | 456 | var i = this.index_or_selected(index); |
|
457 | 457 | var cell = new IPython.CodeCell(this); |
|
458 | 458 | cell.set_input_prompt(); |
|
459 | 459 | this.insert_cell_above(cell, i); |
|
460 | 460 | this.select(this.find_cell_index(cell)); |
|
461 | 461 | return cell; |
|
462 | 462 | } |
|
463 | 463 | |
|
464 | 464 | |
|
465 | 465 | Notebook.prototype.insert_code_cell_below = function (index) { |
|
466 | 466 | // TODO: Bounds check for i |
|
467 | 467 | var i = this.index_or_selected(index); |
|
468 | 468 | var cell = new IPython.CodeCell(this); |
|
469 | 469 | cell.set_input_prompt(); |
|
470 | 470 | this.insert_cell_below(cell, i); |
|
471 | 471 | this.select(this.find_cell_index(cell)); |
|
472 | 472 | return cell; |
|
473 | 473 | } |
|
474 | 474 | |
|
475 | 475 | |
|
476 | 476 | Notebook.prototype.insert_html_cell_above = function (index) { |
|
477 | 477 | // TODO: Bounds check for i |
|
478 | 478 | var i = this.index_or_selected(index); |
|
479 | 479 | var cell = new IPython.HTMLCell(this); |
|
480 | 480 | cell.config_mathjax(); |
|
481 | 481 | this.insert_cell_above(cell, i); |
|
482 | 482 | this.select(this.find_cell_index(cell)); |
|
483 | 483 | return cell; |
|
484 | 484 | } |
|
485 | 485 | |
|
486 | 486 | |
|
487 | 487 | Notebook.prototype.insert_html_cell_below = function (index) { |
|
488 | 488 | // TODO: Bounds check for i |
|
489 | 489 | var i = this.index_or_selected(index); |
|
490 | 490 | var cell = new IPython.HTMLCell(this); |
|
491 | 491 | cell.config_mathjax(); |
|
492 | 492 | this.insert_cell_below(cell, i); |
|
493 | 493 | this.select(this.find_cell_index(cell)); |
|
494 | 494 | return cell; |
|
495 | 495 | } |
|
496 | 496 | |
|
497 | 497 | |
|
498 | 498 | Notebook.prototype.insert_markdown_cell_above = function (index) { |
|
499 | 499 | // TODO: Bounds check for i |
|
500 | 500 | var i = this.index_or_selected(index); |
|
501 | 501 | var cell = new IPython.MarkdownCell(this); |
|
502 | 502 | cell.config_mathjax(); |
|
503 | 503 | this.insert_cell_above(cell, i); |
|
504 | 504 | this.select(this.find_cell_index(cell)); |
|
505 | 505 | return cell; |
|
506 | 506 | } |
|
507 | 507 | |
|
508 | 508 | |
|
509 | 509 | Notebook.prototype.insert_markdown_cell_below = function (index) { |
|
510 | 510 | // TODO: Bounds check for i |
|
511 | 511 | var i = this.index_or_selected(index); |
|
512 | 512 | var cell = new IPython.MarkdownCell(this); |
|
513 | 513 | cell.config_mathjax(); |
|
514 | 514 | this.insert_cell_below(cell, i); |
|
515 | 515 | this.select(this.find_cell_index(cell)); |
|
516 | 516 | return cell; |
|
517 | 517 | } |
|
518 | 518 | |
|
519 | 519 | |
|
520 | 520 | Notebook.prototype.to_code = function (index) { |
|
521 | 521 | // TODO: Bounds check for i |
|
522 | 522 | var i = this.index_or_selected(index); |
|
523 | 523 | var source_element = this.cell_elements().eq(i); |
|
524 | 524 | var source_cell = source_element.data("cell"); |
|
525 | 525 | if (source_cell instanceof IPython.HTMLCell || |
|
526 | 526 | source_cell instanceof IPython.MarkdownCell) { |
|
527 | 527 | this.insert_code_cell_below(i); |
|
528 | 528 | var target_cell = this.cells()[i+1]; |
|
529 | 529 | target_cell.set_code(source_cell.get_source()); |
|
530 | 530 | source_element.remove(); |
|
531 | 531 | target_cell.select(); |
|
532 | 532 | }; |
|
533 | 533 | this.dirty = true; |
|
534 | 534 | }; |
|
535 | 535 | |
|
536 | 536 | |
|
537 | 537 | Notebook.prototype.to_markdown = function (index) { |
|
538 | 538 | // TODO: Bounds check for i |
|
539 | 539 | var i = this.index_or_selected(index); |
|
540 | 540 | var source_element = this.cell_elements().eq(i); |
|
541 | 541 | var source_cell = source_element.data("cell"); |
|
542 | 542 | var target_cell = null; |
|
543 | 543 | if (source_cell instanceof IPython.CodeCell) { |
|
544 | 544 | this.insert_markdown_cell_below(i); |
|
545 | 545 | var target_cell = this.cells()[i+1]; |
|
546 | 546 | var text = source_cell.get_code(); |
|
547 | 547 | } else if (source_cell instanceof IPython.HTMLCell) { |
|
548 | 548 | this.insert_markdown_cell_below(i); |
|
549 | 549 | var target_cell = this.cells()[i+1]; |
|
550 | 550 | var text = source_cell.get_source(); |
|
551 | 551 | if (text === source_cell.placeholder) { |
|
552 | 552 | text = target_cell.placeholder; |
|
553 | 553 | } |
|
554 | 554 | } |
|
555 | 555 | if (target_cell !== null) { |
|
556 | 556 | if (text === "") {text = target_cell.placeholder;}; |
|
557 | 557 | target_cell.set_source(text); |
|
558 | 558 | source_element.remove(); |
|
559 | 559 | target_cell.edit(); |
|
560 | 560 | } |
|
561 | 561 | this.dirty = true; |
|
562 | 562 | }; |
|
563 | 563 | |
|
564 | 564 | |
|
565 | 565 | Notebook.prototype.to_html = function (index) { |
|
566 | 566 | // TODO: Bounds check for i |
|
567 | 567 | var i = this.index_or_selected(index); |
|
568 | 568 | var source_element = this.cell_elements().eq(i); |
|
569 | 569 | var source_cell = source_element.data("cell"); |
|
570 | 570 | var target_cell = null; |
|
571 | 571 | if (source_cell instanceof IPython.CodeCell) { |
|
572 | 572 | this.insert_html_cell_below(i); |
|
573 | 573 | var target_cell = this.cells()[i+1]; |
|
574 | 574 | var text = source_cell.get_code(); |
|
575 | 575 | } else if (source_cell instanceof IPython.MarkdownCell) { |
|
576 | 576 | this.insert_html_cell_below(i); |
|
577 | 577 | var target_cell = this.cells()[i+1]; |
|
578 | 578 | var text = source_cell.get_source(); |
|
579 | 579 | if (text === source_cell.placeholder) { |
|
580 | 580 | text = target_cell.placeholder; |
|
581 | 581 | } |
|
582 | 582 | } |
|
583 | 583 | if (target_cell !== null) { |
|
584 | 584 | if (text === "") {text = target_cell.placeholder;}; |
|
585 | 585 | target_cell.set_source(text); |
|
586 | 586 | source_element.remove(); |
|
587 | 587 | target_cell.edit(); |
|
588 | 588 | } |
|
589 | 589 | this.dirty = true; |
|
590 | 590 | }; |
|
591 | 591 | |
|
592 | 592 | |
|
593 | 593 | // Cell collapsing and output clearing |
|
594 | 594 | |
|
595 | 595 | Notebook.prototype.collapse = function (index) { |
|
596 | 596 | var i = this.index_or_selected(index); |
|
597 | 597 | this.cells()[i].collapse(); |
|
598 | 598 | this.dirty = true; |
|
599 | 599 | }; |
|
600 | 600 | |
|
601 | 601 | |
|
602 | 602 | Notebook.prototype.expand = function (index) { |
|
603 | 603 | var i = this.index_or_selected(index); |
|
604 | 604 | this.cells()[i].expand(); |
|
605 | 605 | this.dirty = true; |
|
606 | 606 | }; |
|
607 | 607 | |
|
608 | 608 | |
|
609 | 609 | Notebook.prototype.toggle_output = function (index) { |
|
610 | 610 | var i = this.index_or_selected(index); |
|
611 | 611 | this.cells()[i].toggle_output(); |
|
612 | 612 | this.dirty = true; |
|
613 | 613 | }; |
|
614 | 614 | |
|
615 | 615 | |
|
616 | 616 | Notebook.prototype.set_autoindent = function (state) { |
|
617 | 617 | var cells = this.cells(); |
|
618 | 618 | len = cells.length; |
|
619 | 619 | for (var i=0; i<len; i++) { |
|
620 | 620 | cells[i].set_autoindent(state) |
|
621 | 621 | }; |
|
622 | 622 | }; |
|
623 | 623 | |
|
624 | 624 | |
|
625 | 625 | Notebook.prototype.clear_all_output = function () { |
|
626 | 626 | var ncells = this.ncells(); |
|
627 | 627 | var cells = this.cells(); |
|
628 | 628 | for (var i=0; i<ncells; i++) { |
|
629 | 629 | if (cells[i] instanceof IPython.CodeCell) { |
|
630 | cells[i].clear_output(); | |
|
630 | cells[i].clear_output(true,true,true); | |
|
631 | 631 | } |
|
632 | 632 | }; |
|
633 | 633 | this.dirty = true; |
|
634 | 634 | }; |
|
635 | 635 | |
|
636 | 636 | // Other cell functions: line numbers, ... |
|
637 | 637 | |
|
638 | 638 | Notebook.prototype.cell_toggle_line_numbers = function() { |
|
639 | 639 | this.selected_cell().toggle_line_numbers() |
|
640 | 640 | }; |
|
641 | 641 | |
|
642 | 642 | // Kernel related things |
|
643 | 643 | |
|
644 | 644 | Notebook.prototype.start_kernel = function () { |
|
645 | 645 | this.kernel = new IPython.Kernel(); |
|
646 | 646 | var notebook_id = IPython.save_widget.get_notebook_id(); |
|
647 | 647 | this.kernel.start(notebook_id, $.proxy(this.kernel_started, this)); |
|
648 | 648 | }; |
|
649 | 649 | |
|
650 | 650 | |
|
651 | 651 | Notebook.prototype.restart_kernel = function () { |
|
652 | 652 | var that = this; |
|
653 | 653 | var notebook_id = IPython.save_widget.get_notebook_id(); |
|
654 | 654 | |
|
655 | 655 | var dialog = $('<div/>'); |
|
656 | 656 | dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.'); |
|
657 | 657 | $(document).append(dialog); |
|
658 | 658 | dialog.dialog({ |
|
659 | 659 | resizable: false, |
|
660 | 660 | modal: true, |
|
661 | 661 | title: "Restart kernel or continue running?", |
|
662 | 662 | buttons : { |
|
663 | 663 | "Restart": function () { |
|
664 | 664 | that.kernel.restart($.proxy(that.kernel_started, that)); |
|
665 | 665 | $(this).dialog('close'); |
|
666 | 666 | }, |
|
667 | 667 | "Continue running": function () { |
|
668 | 668 | $(this).dialog('close'); |
|
669 | 669 | } |
|
670 | 670 | } |
|
671 | 671 | }); |
|
672 | 672 | }; |
|
673 | 673 | |
|
674 | 674 | |
|
675 | 675 | Notebook.prototype.kernel_started = function () { |
|
676 | 676 | console.log("Kernel started: ", this.kernel.kernel_id); |
|
677 | 677 | this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this); |
|
678 | 678 | this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this); |
|
679 | 679 | }; |
|
680 | 680 | |
|
681 | 681 | |
|
682 | 682 | Notebook.prototype.handle_shell_reply = function (e) { |
|
683 | 683 | reply = $.parseJSON(e.data); |
|
684 | 684 | var header = reply.header; |
|
685 | 685 | var content = reply.content; |
|
686 | 686 | var msg_type = header.msg_type; |
|
687 | 687 | // console.log(reply); |
|
688 | 688 | var cell = this.cell_for_msg(reply.parent_header.msg_id); |
|
689 | 689 | if (msg_type === "execute_reply") { |
|
690 | 690 | cell.set_input_prompt(content.execution_count); |
|
691 | 691 | this.dirty = true; |
|
692 | 692 | } else if (msg_type === "complete_reply") { |
|
693 | 693 | cell.finish_completing(content.matched_text, content.matches); |
|
694 | 694 | }; |
|
695 | 695 | var payload = content.payload || []; |
|
696 | 696 | this.handle_payload(cell, payload); |
|
697 | 697 | }; |
|
698 | 698 | |
|
699 | 699 | |
|
700 | 700 | Notebook.prototype.handle_payload = function (cell, payload) { |
|
701 | 701 | var l = payload.length; |
|
702 | 702 | for (var i=0; i<l; i++) { |
|
703 | 703 | if (payload[i].source === 'IPython.zmq.page.page') { |
|
704 | 704 | if (payload[i].text.trim() !== '') { |
|
705 | 705 | IPython.pager.clear(); |
|
706 | 706 | IPython.pager.expand(); |
|
707 | 707 | IPython.pager.append_text(payload[i].text); |
|
708 | 708 | } |
|
709 | 709 | } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') { |
|
710 | 710 | var index = this.find_cell_index(cell); |
|
711 | 711 | var new_cell = this.insert_code_cell_below(index); |
|
712 | 712 | new_cell.set_code(payload[i].text); |
|
713 | 713 | this.dirty = true; |
|
714 | 714 | } |
|
715 | 715 | }; |
|
716 | 716 | }; |
|
717 | 717 | |
|
718 | 718 | |
|
719 | 719 | Notebook.prototype.handle_iopub_reply = function (e) { |
|
720 | 720 | reply = $.parseJSON(e.data); |
|
721 | 721 | var content = reply.content; |
|
722 | 722 | // console.log(reply); |
|
723 | 723 | var msg_type = reply.header.msg_type; |
|
724 | 724 | var cell = this.cell_for_msg(reply.parent_header.msg_id); |
|
725 | 725 | var output_types = ['stream','display_data','pyout','pyerr']; |
|
726 | 726 | if (output_types.indexOf(msg_type) >= 0) { |
|
727 | 727 | this.handle_output(cell, msg_type, content); |
|
728 | 728 | } else if (msg_type === 'status') { |
|
729 | 729 | if (content.execution_state === 'busy') { |
|
730 | 730 | IPython.kernel_status_widget.status_busy(); |
|
731 | 731 | } else if (content.execution_state === 'idle') { |
|
732 | 732 | IPython.kernel_status_widget.status_idle(); |
|
733 | 733 | } else if (content.execution_state === 'dead') { |
|
734 | 734 | this.handle_status_dead(); |
|
735 | 735 | }; |
|
736 | } | |
|
736 | } else if (msg_type === 'clear_output') { | |
|
737 | cell.clear_output(content.stdout, content.stderr, content.other); | |
|
738 | }; | |
|
737 | 739 | }; |
|
738 | 740 | |
|
739 | 741 | |
|
740 | 742 | Notebook.prototype.handle_status_dead = function () { |
|
741 | 743 | var that = this; |
|
742 | 744 | this.kernel.stop_channels(); |
|
743 | 745 | var dialog = $('<div/>'); |
|
744 | 746 | dialog.html('The kernel has died, would you like to restart it? If you do not restart the kernel, you will be able to save the notebook, but running code will not work until the notebook is reopened.'); |
|
745 | 747 | $(document).append(dialog); |
|
746 | 748 | dialog.dialog({ |
|
747 | 749 | resizable: false, |
|
748 | 750 | modal: true, |
|
749 | 751 | title: "Dead kernel", |
|
750 | 752 | buttons : { |
|
751 | 753 | "Restart": function () { |
|
752 | 754 | that.start_kernel(); |
|
753 | 755 | $(this).dialog('close'); |
|
754 | 756 | }, |
|
755 | 757 | "Continue running": function () { |
|
756 | 758 | $(this).dialog('close'); |
|
757 | 759 | } |
|
758 | 760 | } |
|
759 | 761 | }); |
|
760 | 762 | }; |
|
761 | 763 | |
|
762 | 764 | |
|
763 | 765 | Notebook.prototype.handle_output = function (cell, msg_type, content) { |
|
764 | 766 | var json = {}; |
|
765 | 767 | json.output_type = msg_type; |
|
766 | 768 | if (msg_type === "stream") { |
|
767 | 769 | json.text = utils.fixConsole(content.data); |
|
768 | 770 | json.stream = content.name; |
|
769 | 771 | } else if (msg_type === "display_data") { |
|
770 | 772 | json = this.convert_mime_types(json, content.data); |
|
771 | 773 | } else if (msg_type === "pyout") { |
|
772 | 774 | json.prompt_number = content.execution_count; |
|
773 | 775 | json = this.convert_mime_types(json, content.data); |
|
774 | 776 | } else if (msg_type === "pyerr") { |
|
775 | 777 | json.ename = content.ename; |
|
776 | 778 | json.evalue = content.evalue; |
|
777 | 779 | var traceback = []; |
|
778 | 780 | for (var i=0; i<content.traceback.length; i++) { |
|
779 | 781 | traceback.push(utils.fixConsole(content.traceback[i])); |
|
780 | 782 | } |
|
781 | 783 | json.traceback = traceback; |
|
782 | 784 | }; |
|
783 | 785 | cell.append_output(json); |
|
784 | 786 | this.dirty = true; |
|
785 | 787 | }; |
|
786 | 788 | |
|
787 | 789 | |
|
788 | 790 | Notebook.prototype.convert_mime_types = function (json, data) { |
|
789 | 791 | if (data['text/plain'] !== undefined) { |
|
790 | 792 | json.text = utils.fixConsole(data['text/plain']); |
|
791 | 793 | }; |
|
792 | 794 | if (data['text/html'] !== undefined) { |
|
793 | 795 | json.html = data['text/html']; |
|
794 | 796 | }; |
|
795 | 797 | if (data['image/svg+xml'] !== undefined) { |
|
796 | 798 | json.svg = data['image/svg+xml']; |
|
797 | 799 | }; |
|
798 | 800 | if (data['image/png'] !== undefined) { |
|
799 | 801 | json.png = data['image/png']; |
|
800 | 802 | }; |
|
801 | 803 | if (data['image/jpeg'] !== undefined) { |
|
802 | 804 | json.jpeg = data['image/jpeg']; |
|
803 | 805 | }; |
|
804 | 806 | if (data['text/latex'] !== undefined) { |
|
805 | 807 | json.latex = data['text/latex']; |
|
806 | 808 | }; |
|
807 | 809 | if (data['application/json'] !== undefined) { |
|
808 | 810 | json.json = data['application/json']; |
|
809 | 811 | }; |
|
810 | 812 | if (data['application/javascript'] !== undefined) { |
|
811 | 813 | json.javascript = data['application/javascript']; |
|
812 | 814 | } |
|
813 | 815 | return json; |
|
814 | 816 | }; |
|
815 | 817 | |
|
816 | 818 | |
|
817 | 819 | Notebook.prototype.execute_selected_cell = function (options) { |
|
818 | 820 | // add_new: should a new cell be added if we are at the end of the nb |
|
819 | 821 | // terminal: execute in terminal mode, which stays in the current cell |
|
820 | 822 | default_options = {terminal: false, add_new: true} |
|
821 | 823 | $.extend(default_options, options) |
|
822 | 824 | var that = this; |
|
823 | 825 | var cell = that.selected_cell(); |
|
824 | 826 | var cell_index = that.find_cell_index(cell); |
|
825 | 827 | if (cell instanceof IPython.CodeCell) { |
|
826 | cell.clear_output(); | |
|
828 | cell.clear_output(true, true, true); | |
|
827 | 829 | var code = cell.get_code(); |
|
828 | 830 | var msg_id = that.kernel.execute(cell.get_code()); |
|
829 | 831 | that.msg_cell_map[msg_id] = cell.cell_id; |
|
830 | 832 | } else if (cell instanceof IPython.HTMLCell) { |
|
831 | 833 | cell.render(); |
|
832 | 834 | } |
|
833 | 835 | if (default_options.terminal) { |
|
834 | 836 | cell.select_all(); |
|
835 | 837 | } else { |
|
836 | 838 | if ((cell_index === (that.ncells()-1)) && default_options.add_new) { |
|
837 | 839 | that.insert_code_cell_below(); |
|
838 | 840 | // If we are adding a new cell at the end, scroll down to show it. |
|
839 | 841 | that.scroll_to_bottom(); |
|
840 | 842 | } else { |
|
841 | 843 | that.select(cell_index+1); |
|
842 | 844 | }; |
|
843 | 845 | }; |
|
844 | 846 | this.dirty = true; |
|
845 | 847 | }; |
|
846 | 848 | |
|
847 | 849 | |
|
848 | 850 | Notebook.prototype.execute_all_cells = function () { |
|
849 | 851 | var ncells = this.ncells(); |
|
850 | 852 | for (var i=0; i<ncells; i++) { |
|
851 | 853 | this.select(i); |
|
852 | 854 | this.execute_selected_cell({add_new:false}); |
|
853 | 855 | }; |
|
854 | 856 | this.scroll_to_bottom(); |
|
855 | 857 | }; |
|
856 | 858 | |
|
857 | 859 | |
|
858 | 860 | Notebook.prototype.complete_cell = function (cell, line, cursor_pos) { |
|
859 | 861 | var msg_id = this.kernel.complete(line, cursor_pos); |
|
860 | 862 | this.msg_cell_map[msg_id] = cell.cell_id; |
|
861 | 863 | }; |
|
862 | 864 | |
|
863 | 865 | // Persistance and loading |
|
864 | 866 | |
|
865 | 867 | |
|
866 | 868 | Notebook.prototype.fromJSON = function (data) { |
|
867 | 869 | var ncells = this.ncells(); |
|
868 | 870 | for (var i=0; i<ncells; i++) { |
|
869 | 871 | // Always delete cell 0 as they get renumbered as they are deleted. |
|
870 | 872 | this.delete_cell(0); |
|
871 | 873 | }; |
|
872 | 874 | // Save the metadata |
|
873 | 875 | this.metadata = data.metadata; |
|
874 | 876 | // Only handle 1 worksheet for now. |
|
875 | 877 | var worksheet = data.worksheets[0]; |
|
876 | 878 | if (worksheet !== undefined) { |
|
877 | 879 | var new_cells = worksheet.cells; |
|
878 | 880 | ncells = new_cells.length; |
|
879 | 881 | var cell_data = null; |
|
880 | 882 | var new_cell = null; |
|
881 | 883 | for (var i=0; i<ncells; i++) { |
|
882 | 884 | cell_data = new_cells[i]; |
|
883 | 885 | if (cell_data.cell_type == 'code') { |
|
884 | 886 | new_cell = this.insert_code_cell_below(); |
|
885 | 887 | new_cell.fromJSON(cell_data); |
|
886 | 888 | } else if (cell_data.cell_type === 'html') { |
|
887 | 889 | new_cell = this.insert_html_cell_below(); |
|
888 | 890 | new_cell.fromJSON(cell_data); |
|
889 | 891 | } else if (cell_data.cell_type === 'markdown') { |
|
890 | 892 | new_cell = this.insert_markdown_cell_below(); |
|
891 | 893 | new_cell.fromJSON(cell_data); |
|
892 | 894 | }; |
|
893 | 895 | }; |
|
894 | 896 | }; |
|
895 | 897 | }; |
|
896 | 898 | |
|
897 | 899 | |
|
898 | 900 | Notebook.prototype.toJSON = function () { |
|
899 | 901 | var cells = this.cells(); |
|
900 | 902 | var ncells = cells.length; |
|
901 | 903 | cell_array = new Array(ncells); |
|
902 | 904 | for (var i=0; i<ncells; i++) { |
|
903 | 905 | cell_array[i] = cells[i].toJSON(); |
|
904 | 906 | }; |
|
905 | 907 | data = { |
|
906 | 908 | // Only handle 1 worksheet for now. |
|
907 | 909 | worksheets : [{cells:cell_array}], |
|
908 | 910 | metadata : this.metadata |
|
909 | 911 | } |
|
910 | 912 | return data |
|
911 | 913 | }; |
|
912 | 914 | |
|
913 | 915 | Notebook.prototype.save_notebook = function () { |
|
914 | 916 | if (IPython.save_widget.test_notebook_name()) { |
|
915 | 917 | var notebook_id = IPython.save_widget.get_notebook_id(); |
|
916 | 918 | var nbname = IPython.save_widget.get_notebook_name(); |
|
917 | 919 | // We may want to move the name/id/nbformat logic inside toJSON? |
|
918 | 920 | var data = this.toJSON(); |
|
919 | 921 | data.metadata.name = nbname; |
|
920 | 922 | data.nbformat = 2; |
|
921 | 923 | // We do the call with settings so we can set cache to false. |
|
922 | 924 | var settings = { |
|
923 | 925 | processData : false, |
|
924 | 926 | cache : false, |
|
925 | 927 | type : "PUT", |
|
926 | 928 | data : JSON.stringify(data), |
|
927 | 929 | headers : {'Content-Type': 'application/json'}, |
|
928 | 930 | success : $.proxy(this.notebook_saved,this), |
|
929 | 931 | error : $.proxy(this.notebook_save_failed,this) |
|
930 | 932 | }; |
|
931 | 933 | IPython.save_widget.status_saving(); |
|
932 | 934 | $.ajax("/notebooks/" + notebook_id, settings); |
|
933 | 935 | }; |
|
934 | 936 | }; |
|
935 | 937 | |
|
936 | 938 | |
|
937 | 939 | Notebook.prototype.notebook_saved = function (data, status, xhr) { |
|
938 | 940 | this.dirty = false; |
|
939 | 941 | IPython.save_widget.notebook_saved(); |
|
940 | 942 | setTimeout($.proxy(IPython.save_widget.status_save,IPython.save_widget),500); |
|
941 | 943 | } |
|
942 | 944 | |
|
943 | 945 | |
|
944 | 946 | Notebook.prototype.notebook_save_failed = function (xhr, status, error_msg) { |
|
945 | 947 | // Notify the user and reset the save button |
|
946 | 948 | // TODO: Handle different types of errors (timeout etc.) |
|
947 | 949 | alert('An unexpected error occured while saving the notebook.'); |
|
948 | 950 | setTimeout($.proxy(IPython.save_widget.reset_status,IPython.save_widget),500); |
|
949 | 951 | } |
|
950 | 952 | |
|
951 | 953 | |
|
952 | 954 | Notebook.prototype.load_notebook = function (callback) { |
|
953 | 955 | var that = this; |
|
954 | 956 | var notebook_id = IPython.save_widget.get_notebook_id(); |
|
955 | 957 | // We do the call with settings so we can set cache to false. |
|
956 | 958 | var settings = { |
|
957 | 959 | processData : false, |
|
958 | 960 | cache : false, |
|
959 | 961 | type : "GET", |
|
960 | 962 | dataType : "json", |
|
961 | 963 | success : function (data, status, xhr) { |
|
962 | 964 | that.notebook_loaded(data, status, xhr); |
|
963 | 965 | if (callback !== undefined) { |
|
964 | 966 | callback(); |
|
965 | 967 | }; |
|
966 | 968 | } |
|
967 | 969 | }; |
|
968 | 970 | IPython.save_widget.status_loading(); |
|
969 | 971 | $.ajax("/notebooks/" + notebook_id, settings); |
|
970 | 972 | } |
|
971 | 973 | |
|
972 | 974 | |
|
973 | 975 | Notebook.prototype.notebook_loaded = function (data, status, xhr) { |
|
974 | 976 | this.fromJSON(data); |
|
975 | 977 | if (this.ncells() === 0) { |
|
976 | 978 | this.insert_code_cell_below(); |
|
977 | 979 | }; |
|
978 | 980 | IPython.save_widget.status_save(); |
|
979 | 981 | IPython.save_widget.set_notebook_name(data.metadata.name); |
|
980 | 982 | this.start_kernel(); |
|
981 | 983 | this.dirty = false; |
|
982 | 984 | // fromJSON always selects the last cell inserted. We need to wait |
|
983 | 985 | // until that is done before scrolling to the top. |
|
984 | 986 | setTimeout(function () { |
|
985 | 987 | IPython.notebook.select(0); |
|
986 | 988 | IPython.notebook.scroll_to_top(); |
|
987 | 989 | }, 50); |
|
988 | 990 | }; |
|
989 | 991 | |
|
990 | 992 | IPython.Notebook = Notebook; |
|
991 | 993 | |
|
992 | 994 | return IPython; |
|
993 | 995 | |
|
994 | 996 | }(IPython)); |
|
995 | 997 |
@@ -1,489 +1,495 b'' | |||
|
1 | 1 | """A ZMQ-based subclass of InteractiveShell. |
|
2 | 2 | |
|
3 | 3 | This code is meant to ease the refactoring of the base InteractiveShell into |
|
4 | 4 | something with a cleaner architecture for 2-process use, without actually |
|
5 | 5 | breaking InteractiveShell itself. So we're doing something a bit ugly, where |
|
6 | 6 | we subclass and override what we want to fix. Once this is working well, we |
|
7 | 7 | can go back to the base class and refactor the code for a cleaner inheritance |
|
8 | 8 | implementation that doesn't rely on so much monkeypatching. |
|
9 | 9 | |
|
10 | 10 | But this lets us maintain a fully working IPython as we develop the new |
|
11 | 11 | machinery. This should thus be thought of as scaffolding. |
|
12 | 12 | """ |
|
13 | 13 | #----------------------------------------------------------------------------- |
|
14 | 14 | # Imports |
|
15 | 15 | #----------------------------------------------------------------------------- |
|
16 | 16 | from __future__ import print_function |
|
17 | 17 | |
|
18 | 18 | # Stdlib |
|
19 | 19 | import inspect |
|
20 | 20 | import os |
|
21 | 21 | import sys |
|
22 | 22 | from subprocess import Popen, PIPE |
|
23 | 23 | |
|
24 | 24 | # Our own |
|
25 | 25 | from IPython.core.interactiveshell import ( |
|
26 | 26 | InteractiveShell, InteractiveShellABC |
|
27 | 27 | ) |
|
28 | 28 | from IPython.core import page |
|
29 | 29 | from IPython.core.autocall import ZMQExitAutocall |
|
30 | 30 | from IPython.core.displaypub import DisplayPublisher |
|
31 | 31 | from IPython.core.macro import Macro |
|
32 | 32 | from IPython.core.magic import MacroToEdit |
|
33 | 33 | from IPython.core.payloadpage import install_payload_page |
|
34 | 34 | from IPython.lib.kernel import ( |
|
35 | 35 | get_connection_file, get_connection_info, connect_qtconsole |
|
36 | 36 | ) |
|
37 | 37 | from IPython.utils import io |
|
38 | 38 | from IPython.utils.jsonutil import json_clean |
|
39 | 39 | from IPython.utils.path import get_py_filename |
|
40 | 40 | from IPython.utils.process import arg_split |
|
41 | 41 | from IPython.utils.traitlets import Instance, Type, Dict, CBool |
|
42 | 42 | from IPython.utils.warn import warn, error |
|
43 | 43 | from IPython.zmq.displayhook import ZMQShellDisplayHook, _encode_binary |
|
44 | 44 | from IPython.zmq.session import extract_header |
|
45 | 45 | from session import Session |
|
46 | 46 | |
|
47 | 47 | #----------------------------------------------------------------------------- |
|
48 | 48 | # Globals and side-effects |
|
49 | 49 | #----------------------------------------------------------------------------- |
|
50 | 50 | |
|
51 | 51 | # Install the payload version of page. |
|
52 | 52 | install_payload_page() |
|
53 | 53 | |
|
54 | 54 | #----------------------------------------------------------------------------- |
|
55 | 55 | # Functions and classes |
|
56 | 56 | #----------------------------------------------------------------------------- |
|
57 | 57 | |
|
58 | 58 | class ZMQDisplayPublisher(DisplayPublisher): |
|
59 | 59 | """A display publisher that publishes data using a ZeroMQ PUB socket.""" |
|
60 | 60 | |
|
61 | 61 | session = Instance(Session) |
|
62 | 62 | pub_socket = Instance('zmq.Socket') |
|
63 | 63 | parent_header = Dict({}) |
|
64 | 64 | |
|
65 | 65 | def set_parent(self, parent): |
|
66 | 66 | """Set the parent for outbound messages.""" |
|
67 | 67 | self.parent_header = extract_header(parent) |
|
68 | 68 | |
|
69 | 69 | def publish(self, source, data, metadata=None): |
|
70 | 70 | if metadata is None: |
|
71 | 71 | metadata = {} |
|
72 | 72 | self._validate_data(source, data, metadata) |
|
73 | 73 | content = {} |
|
74 | 74 | content['source'] = source |
|
75 | 75 | _encode_binary(data) |
|
76 | 76 | content['data'] = data |
|
77 | 77 | content['metadata'] = metadata |
|
78 | 78 | self.session.send( |
|
79 | 79 | self.pub_socket, u'display_data', json_clean(content), |
|
80 | 80 | parent=self.parent_header |
|
81 | 81 | ) |
|
82 | 82 | |
|
83 | def clear_output(self, stdout=True, stderr=True, other=True): | |
|
84 | content = dict(stdout=stdout, stderr=stderr, other=other) | |
|
85 | self.session.send( | |
|
86 | self.pub_socket, u'clear_output', content, | |
|
87 | parent=self.parent_header | |
|
88 | ) | |
|
83 | 89 | |
|
84 | 90 | class ZMQInteractiveShell(InteractiveShell): |
|
85 | 91 | """A subclass of InteractiveShell for ZMQ.""" |
|
86 | 92 | |
|
87 | 93 | displayhook_class = Type(ZMQShellDisplayHook) |
|
88 | 94 | display_pub_class = Type(ZMQDisplayPublisher) |
|
89 | 95 | |
|
90 | 96 | # Override the traitlet in the parent class, because there's no point using |
|
91 | 97 | # readline for the kernel. Can be removed when the readline code is moved |
|
92 | 98 | # to the terminal frontend. |
|
93 | 99 | colors_force = CBool(True) |
|
94 | 100 | readline_use = CBool(False) |
|
95 | 101 | # autoindent has no meaning in a zmqshell, and attempting to enable it |
|
96 | 102 | # will print a warning in the absence of readline. |
|
97 | 103 | autoindent = CBool(False) |
|
98 | 104 | |
|
99 | 105 | exiter = Instance(ZMQExitAutocall) |
|
100 | 106 | def _exiter_default(self): |
|
101 | 107 | return ZMQExitAutocall(self) |
|
102 | 108 | |
|
103 | 109 | keepkernel_on_exit = None |
|
104 | 110 | |
|
105 | 111 | def init_environment(self): |
|
106 | 112 | """Configure the user's environment. |
|
107 | 113 | |
|
108 | 114 | """ |
|
109 | 115 | env = os.environ |
|
110 | 116 | # These two ensure 'ls' produces nice coloring on BSD-derived systems |
|
111 | 117 | env['TERM'] = 'xterm-color' |
|
112 | 118 | env['CLICOLOR'] = '1' |
|
113 | 119 | # Since normal pagers don't work at all (over pexpect we don't have |
|
114 | 120 | # single-key control of the subprocess), try to disable paging in |
|
115 | 121 | # subprocesses as much as possible. |
|
116 | 122 | env['PAGER'] = 'cat' |
|
117 | 123 | env['GIT_PAGER'] = 'cat' |
|
118 | 124 | |
|
119 | 125 | def auto_rewrite_input(self, cmd): |
|
120 | 126 | """Called to show the auto-rewritten input for autocall and friends. |
|
121 | 127 | |
|
122 | 128 | FIXME: this payload is currently not correctly processed by the |
|
123 | 129 | frontend. |
|
124 | 130 | """ |
|
125 | 131 | new = self.displayhook.prompt1.auto_rewrite() + cmd |
|
126 | 132 | payload = dict( |
|
127 | 133 | source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input', |
|
128 | 134 | transformed_input=new, |
|
129 | 135 | ) |
|
130 | 136 | self.payload_manager.write_payload(payload) |
|
131 | 137 | |
|
132 | 138 | def ask_exit(self): |
|
133 | 139 | """Engage the exit actions.""" |
|
134 | 140 | payload = dict( |
|
135 | 141 | source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit', |
|
136 | 142 | exit=True, |
|
137 | 143 | keepkernel=self.keepkernel_on_exit, |
|
138 | 144 | ) |
|
139 | 145 | self.payload_manager.write_payload(payload) |
|
140 | 146 | |
|
141 | 147 | def _showtraceback(self, etype, evalue, stb): |
|
142 | 148 | |
|
143 | 149 | exc_content = { |
|
144 | 150 | u'traceback' : stb, |
|
145 | 151 | u'ename' : unicode(etype.__name__), |
|
146 | 152 | u'evalue' : unicode(evalue) |
|
147 | 153 | } |
|
148 | 154 | |
|
149 | 155 | dh = self.displayhook |
|
150 | 156 | # Send exception info over pub socket for other clients than the caller |
|
151 | 157 | # to pick up |
|
152 | 158 | exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header) |
|
153 | 159 | |
|
154 | 160 | # FIXME - Hack: store exception info in shell object. Right now, the |
|
155 | 161 | # caller is reading this info after the fact, we need to fix this logic |
|
156 | 162 | # to remove this hack. Even uglier, we need to store the error status |
|
157 | 163 | # here, because in the main loop, the logic that sets it is being |
|
158 | 164 | # skipped because runlines swallows the exceptions. |
|
159 | 165 | exc_content[u'status'] = u'error' |
|
160 | 166 | self._reply_content = exc_content |
|
161 | 167 | # /FIXME |
|
162 | 168 | |
|
163 | 169 | return exc_content |
|
164 | 170 | |
|
165 | 171 | #------------------------------------------------------------------------ |
|
166 | 172 | # Magic overrides |
|
167 | 173 | #------------------------------------------------------------------------ |
|
168 | 174 | # Once the base class stops inheriting from magic, this code needs to be |
|
169 | 175 | # moved into a separate machinery as well. For now, at least isolate here |
|
170 | 176 | # the magics which this class needs to implement differently from the base |
|
171 | 177 | # class, or that are unique to it. |
|
172 | 178 | |
|
173 | 179 | def magic_doctest_mode(self,parameter_s=''): |
|
174 | 180 | """Toggle doctest mode on and off. |
|
175 | 181 | |
|
176 | 182 | This mode is intended to make IPython behave as much as possible like a |
|
177 | 183 | plain Python shell, from the perspective of how its prompts, exceptions |
|
178 | 184 | and output look. This makes it easy to copy and paste parts of a |
|
179 | 185 | session into doctests. It does so by: |
|
180 | 186 | |
|
181 | 187 | - Changing the prompts to the classic ``>>>`` ones. |
|
182 | 188 | - Changing the exception reporting mode to 'Plain'. |
|
183 | 189 | - Disabling pretty-printing of output. |
|
184 | 190 | |
|
185 | 191 | Note that IPython also supports the pasting of code snippets that have |
|
186 | 192 | leading '>>>' and '...' prompts in them. This means that you can paste |
|
187 | 193 | doctests from files or docstrings (even if they have leading |
|
188 | 194 | whitespace), and the code will execute correctly. You can then use |
|
189 | 195 | '%history -t' to see the translated history; this will give you the |
|
190 | 196 | input after removal of all the leading prompts and whitespace, which |
|
191 | 197 | can be pasted back into an editor. |
|
192 | 198 | |
|
193 | 199 | With these features, you can switch into this mode easily whenever you |
|
194 | 200 | need to do testing and changes to doctests, without having to leave |
|
195 | 201 | your existing IPython session. |
|
196 | 202 | """ |
|
197 | 203 | |
|
198 | 204 | from IPython.utils.ipstruct import Struct |
|
199 | 205 | |
|
200 | 206 | # Shorthands |
|
201 | 207 | shell = self.shell |
|
202 | 208 | disp_formatter = self.shell.display_formatter |
|
203 | 209 | ptformatter = disp_formatter.formatters['text/plain'] |
|
204 | 210 | # dstore is a data store kept in the instance metadata bag to track any |
|
205 | 211 | # changes we make, so we can undo them later. |
|
206 | 212 | dstore = shell.meta.setdefault('doctest_mode', Struct()) |
|
207 | 213 | save_dstore = dstore.setdefault |
|
208 | 214 | |
|
209 | 215 | # save a few values we'll need to recover later |
|
210 | 216 | mode = save_dstore('mode', False) |
|
211 | 217 | save_dstore('rc_pprint', ptformatter.pprint) |
|
212 | 218 | save_dstore('rc_plain_text_only',disp_formatter.plain_text_only) |
|
213 | 219 | save_dstore('xmode', shell.InteractiveTB.mode) |
|
214 | 220 | |
|
215 | 221 | if mode == False: |
|
216 | 222 | # turn on |
|
217 | 223 | ptformatter.pprint = False |
|
218 | 224 | disp_formatter.plain_text_only = True |
|
219 | 225 | shell.magic_xmode('Plain') |
|
220 | 226 | else: |
|
221 | 227 | # turn off |
|
222 | 228 | ptformatter.pprint = dstore.rc_pprint |
|
223 | 229 | disp_formatter.plain_text_only = dstore.rc_plain_text_only |
|
224 | 230 | shell.magic_xmode(dstore.xmode) |
|
225 | 231 | |
|
226 | 232 | # Store new mode and inform on console |
|
227 | 233 | dstore.mode = bool(1-int(mode)) |
|
228 | 234 | mode_label = ['OFF','ON'][dstore.mode] |
|
229 | 235 | print('Doctest mode is:', mode_label) |
|
230 | 236 | |
|
231 | 237 | # Send the payload back so that clients can modify their prompt display |
|
232 | 238 | payload = dict( |
|
233 | 239 | source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_doctest_mode', |
|
234 | 240 | mode=dstore.mode) |
|
235 | 241 | self.payload_manager.write_payload(payload) |
|
236 | 242 | |
|
237 | 243 | def magic_edit(self,parameter_s='',last_call=['','']): |
|
238 | 244 | """Bring up an editor and execute the resulting code. |
|
239 | 245 | |
|
240 | 246 | Usage: |
|
241 | 247 | %edit [options] [args] |
|
242 | 248 | |
|
243 | 249 | %edit runs an external text editor. You will need to set the command for |
|
244 | 250 | this editor via the ``TerminalInteractiveShell.editor`` option in your |
|
245 | 251 | configuration file before it will work. |
|
246 | 252 | |
|
247 | 253 | This command allows you to conveniently edit multi-line code right in |
|
248 | 254 | your IPython session. |
|
249 | 255 | |
|
250 | 256 | If called without arguments, %edit opens up an empty editor with a |
|
251 | 257 | temporary file and will execute the contents of this file when you |
|
252 | 258 | close it (don't forget to save it!). |
|
253 | 259 | |
|
254 | 260 | |
|
255 | 261 | Options: |
|
256 | 262 | |
|
257 | 263 | -n <number>: open the editor at a specified line number. By default, |
|
258 | 264 | the IPython editor hook uses the unix syntax 'editor +N filename', but |
|
259 | 265 | you can configure this by providing your own modified hook if your |
|
260 | 266 | favorite editor supports line-number specifications with a different |
|
261 | 267 | syntax. |
|
262 | 268 | |
|
263 | 269 | -p: this will call the editor with the same data as the previous time |
|
264 | 270 | it was used, regardless of how long ago (in your current session) it |
|
265 | 271 | was. |
|
266 | 272 | |
|
267 | 273 | -r: use 'raw' input. This option only applies to input taken from the |
|
268 | 274 | user's history. By default, the 'processed' history is used, so that |
|
269 | 275 | magics are loaded in their transformed version to valid Python. If |
|
270 | 276 | this option is given, the raw input as typed as the command line is |
|
271 | 277 | used instead. When you exit the editor, it will be executed by |
|
272 | 278 | IPython's own processor. |
|
273 | 279 | |
|
274 | 280 | -x: do not execute the edited code immediately upon exit. This is |
|
275 | 281 | mainly useful if you are editing programs which need to be called with |
|
276 | 282 | command line arguments, which you can then do using %run. |
|
277 | 283 | |
|
278 | 284 | |
|
279 | 285 | Arguments: |
|
280 | 286 | |
|
281 | 287 | If arguments are given, the following possibilites exist: |
|
282 | 288 | |
|
283 | 289 | - The arguments are numbers or pairs of colon-separated numbers (like |
|
284 | 290 | 1 4:8 9). These are interpreted as lines of previous input to be |
|
285 | 291 | loaded into the editor. The syntax is the same of the %macro command. |
|
286 | 292 | |
|
287 | 293 | - If the argument doesn't start with a number, it is evaluated as a |
|
288 | 294 | variable and its contents loaded into the editor. You can thus edit |
|
289 | 295 | any string which contains python code (including the result of |
|
290 | 296 | previous edits). |
|
291 | 297 | |
|
292 | 298 | - If the argument is the name of an object (other than a string), |
|
293 | 299 | IPython will try to locate the file where it was defined and open the |
|
294 | 300 | editor at the point where it is defined. You can use `%edit function` |
|
295 | 301 | to load an editor exactly at the point where 'function' is defined, |
|
296 | 302 | edit it and have the file be executed automatically. |
|
297 | 303 | |
|
298 | 304 | If the object is a macro (see %macro for details), this opens up your |
|
299 | 305 | specified editor with a temporary file containing the macro's data. |
|
300 | 306 | Upon exit, the macro is reloaded with the contents of the file. |
|
301 | 307 | |
|
302 | 308 | Note: opening at an exact line is only supported under Unix, and some |
|
303 | 309 | editors (like kedit and gedit up to Gnome 2.8) do not understand the |
|
304 | 310 | '+NUMBER' parameter necessary for this feature. Good editors like |
|
305 | 311 | (X)Emacs, vi, jed, pico and joe all do. |
|
306 | 312 | |
|
307 | 313 | - If the argument is not found as a variable, IPython will look for a |
|
308 | 314 | file with that name (adding .py if necessary) and load it into the |
|
309 | 315 | editor. It will execute its contents with execfile() when you exit, |
|
310 | 316 | loading any code in the file into your interactive namespace. |
|
311 | 317 | |
|
312 | 318 | After executing your code, %edit will return as output the code you |
|
313 | 319 | typed in the editor (except when it was an existing file). This way |
|
314 | 320 | you can reload the code in further invocations of %edit as a variable, |
|
315 | 321 | via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of |
|
316 | 322 | the output. |
|
317 | 323 | |
|
318 | 324 | Note that %edit is also available through the alias %ed. |
|
319 | 325 | |
|
320 | 326 | This is an example of creating a simple function inside the editor and |
|
321 | 327 | then modifying it. First, start up the editor: |
|
322 | 328 | |
|
323 | 329 | In [1]: ed |
|
324 | 330 | Editing... done. Executing edited code... |
|
325 | 331 | Out[1]: 'def foo():n print "foo() was defined in an editing session"n' |
|
326 | 332 | |
|
327 | 333 | We can then call the function foo(): |
|
328 | 334 | |
|
329 | 335 | In [2]: foo() |
|
330 | 336 | foo() was defined in an editing session |
|
331 | 337 | |
|
332 | 338 | Now we edit foo. IPython automatically loads the editor with the |
|
333 | 339 | (temporary) file where foo() was previously defined: |
|
334 | 340 | |
|
335 | 341 | In [3]: ed foo |
|
336 | 342 | Editing... done. Executing edited code... |
|
337 | 343 | |
|
338 | 344 | And if we call foo() again we get the modified version: |
|
339 | 345 | |
|
340 | 346 | In [4]: foo() |
|
341 | 347 | foo() has now been changed! |
|
342 | 348 | |
|
343 | 349 | Here is an example of how to edit a code snippet successive |
|
344 | 350 | times. First we call the editor: |
|
345 | 351 | |
|
346 | 352 | In [5]: ed |
|
347 | 353 | Editing... done. Executing edited code... |
|
348 | 354 | hello |
|
349 | 355 | Out[5]: "print 'hello'n" |
|
350 | 356 | |
|
351 | 357 | Now we call it again with the previous output (stored in _): |
|
352 | 358 | |
|
353 | 359 | In [6]: ed _ |
|
354 | 360 | Editing... done. Executing edited code... |
|
355 | 361 | hello world |
|
356 | 362 | Out[6]: "print 'hello world'n" |
|
357 | 363 | |
|
358 | 364 | Now we call it with the output #8 (stored in _8, also as Out[8]): |
|
359 | 365 | |
|
360 | 366 | In [7]: ed _8 |
|
361 | 367 | Editing... done. Executing edited code... |
|
362 | 368 | hello again |
|
363 | 369 | Out[7]: "print 'hello again'n" |
|
364 | 370 | """ |
|
365 | 371 | |
|
366 | 372 | opts,args = self.parse_options(parameter_s,'prn:') |
|
367 | 373 | |
|
368 | 374 | try: |
|
369 | 375 | filename, lineno, _ = self._find_edit_target(args, opts, last_call) |
|
370 | 376 | except MacroToEdit as e: |
|
371 | 377 | # TODO: Implement macro editing over 2 processes. |
|
372 | 378 | print("Macro editing not yet implemented in 2-process model.") |
|
373 | 379 | return |
|
374 | 380 | |
|
375 | 381 | # Make sure we send to the client an absolute path, in case the working |
|
376 | 382 | # directory of client and kernel don't match |
|
377 | 383 | filename = os.path.abspath(filename) |
|
378 | 384 | |
|
379 | 385 | payload = { |
|
380 | 386 | 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic', |
|
381 | 387 | 'filename' : filename, |
|
382 | 388 | 'line_number' : lineno |
|
383 | 389 | } |
|
384 | 390 | self.payload_manager.write_payload(payload) |
|
385 | 391 | |
|
386 | 392 | def magic_gui(self, *args, **kwargs): |
|
387 | 393 | raise NotImplementedError( |
|
388 | 394 | 'Kernel GUI support is not implemented yet, except for --pylab.') |
|
389 | 395 | |
|
390 | 396 | def magic_pylab(self, *args, **kwargs): |
|
391 | 397 | raise NotImplementedError( |
|
392 | 398 | 'pylab support must be enabled in command line options.') |
|
393 | 399 | |
|
394 | 400 | # A few magics that are adapted to the specifics of using pexpect and a |
|
395 | 401 | # remote terminal |
|
396 | 402 | |
|
397 | 403 | def magic_clear(self, arg_s): |
|
398 | 404 | """Clear the terminal.""" |
|
399 | 405 | if os.name == 'posix': |
|
400 | 406 | self.shell.system("clear") |
|
401 | 407 | else: |
|
402 | 408 | self.shell.system("cls") |
|
403 | 409 | |
|
404 | 410 | if os.name == 'nt': |
|
405 | 411 | # This is the usual name in windows |
|
406 | 412 | magic_cls = magic_clear |
|
407 | 413 | |
|
408 | 414 | # Terminal pagers won't work over pexpect, but we do have our own pager |
|
409 | 415 | |
|
410 | 416 | def magic_less(self, arg_s): |
|
411 | 417 | """Show a file through the pager. |
|
412 | 418 | |
|
413 | 419 | Files ending in .py are syntax-highlighted.""" |
|
414 | 420 | cont = open(arg_s).read() |
|
415 | 421 | if arg_s.endswith('.py'): |
|
416 | 422 | cont = self.shell.pycolorize(cont) |
|
417 | 423 | page.page(cont) |
|
418 | 424 | |
|
419 | 425 | magic_more = magic_less |
|
420 | 426 | |
|
421 | 427 | # Man calls a pager, so we also need to redefine it |
|
422 | 428 | if os.name == 'posix': |
|
423 | 429 | def magic_man(self, arg_s): |
|
424 | 430 | """Find the man page for the given command and display in pager.""" |
|
425 | 431 | page.page(self.shell.getoutput('man %s | col -b' % arg_s, |
|
426 | 432 | split=False)) |
|
427 | 433 | |
|
428 | 434 | # FIXME: this is specific to the GUI, so we should let the gui app load |
|
429 | 435 | # magics at startup that are only for the gui. Once the gui app has proper |
|
430 | 436 | # profile and configuration management, we can have it initialize a kernel |
|
431 | 437 | # with a special config file that provides these. |
|
432 | 438 | def magic_guiref(self, arg_s): |
|
433 | 439 | """Show a basic reference about the GUI console.""" |
|
434 | 440 | from IPython.core.usage import gui_reference |
|
435 | 441 | page.page(gui_reference, auto_html=True) |
|
436 | 442 | |
|
437 | 443 | def magic_connect_info(self, arg_s): |
|
438 | 444 | """Print information for connecting other clients to this kernel |
|
439 | 445 | |
|
440 | 446 | It will print the contents of this session's connection file, as well as |
|
441 | 447 | shortcuts for local clients. |
|
442 | 448 | |
|
443 | 449 | In the simplest case, when called from the most recently launched kernel, |
|
444 | 450 | secondary clients can be connected, simply with: |
|
445 | 451 | |
|
446 | 452 | $> ipython <app> --existing |
|
447 | 453 | |
|
448 | 454 | """ |
|
449 | 455 | try: |
|
450 | 456 | connection_file = get_connection_file() |
|
451 | 457 | info = get_connection_info(unpack=False) |
|
452 | 458 | except Exception as e: |
|
453 | 459 | error("Could not get connection info: %r" % e) |
|
454 | 460 | return |
|
455 | 461 | |
|
456 | 462 | print (info + '\n') |
|
457 | 463 | print ("Paste the above JSON into a file, and connect with:\n" |
|
458 | 464 | " $> ipython <app> --existing <file>\n" |
|
459 | 465 | "or, if you are local, you can connect with just:\n" |
|
460 | 466 | " $> ipython <app> --existing %s\n" |
|
461 | 467 | "or even just:\n" |
|
462 | 468 | " $> ipython <app> --existing\n" |
|
463 | 469 | "if this is the most recent IPython session you have started." |
|
464 | 470 | % os.path.basename(connection_file) |
|
465 | 471 | ) |
|
466 | 472 | |
|
467 | 473 | def magic_qtconsole(self, arg_s): |
|
468 | 474 | """Open a qtconsole connected to this kernel. |
|
469 | 475 | |
|
470 | 476 | Useful for connecting a qtconsole to running notebooks, for better |
|
471 | 477 | debugging. |
|
472 | 478 | """ |
|
473 | 479 | try: |
|
474 | 480 | p = connect_qtconsole(argv=arg_split(arg_s, os.name=='posix')) |
|
475 | 481 | except Exception as e: |
|
476 | 482 | error("Could not start qtconsole: %r" % e) |
|
477 | 483 | return |
|
478 | 484 | |
|
479 | 485 | |
|
480 | 486 | def set_next_input(self, text): |
|
481 | 487 | """Send the specified text to the frontend to be presented at the next |
|
482 | 488 | input cell.""" |
|
483 | 489 | payload = dict( |
|
484 | 490 | source='IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input', |
|
485 | 491 | text=text |
|
486 | 492 | ) |
|
487 | 493 | self.payload_manager.write_payload(payload) |
|
488 | 494 | |
|
489 | 495 | InteractiveShellABC.register(ZMQInteractiveShell) |
General Comments 0
You need to be logged in to leave comments.
Login now