mirror of
https://gitdl.cn/https://github.com/chakralinux/core.git
synced 2025-02-19 12:25:33 +08:00
11326 lines
405 KiB
Diff
11326 lines
405 KiB
Diff
diff --git a/src/bin/sage-ipython b/src/bin/sage-ipython
|
|
index 2a66e09..060212e 100755
|
|
--- a/src/bin/sage-ipython
|
|
+++ b/src/bin/sage-ipython
|
|
@@ -3,6 +3,7 @@
|
|
"""
|
|
Sage IPython startup script.
|
|
"""
|
|
+
|
|
from sage.repl.interpreter import SageTerminalApp
|
|
|
|
# installs the extra readline commands before the IPython initialization begins.
|
|
diff --git a/src/bin/sage-notebook b/src/bin/sage-notebook
|
|
index e75e190..bdc41b7 100755
|
|
--- a/src/bin/sage-notebook
|
|
+++ b/src/bin/sage-notebook
|
|
@@ -74,13 +74,14 @@ class NotebookIPython(object):
|
|
""")
|
|
|
|
def __init__(self, argv):
|
|
- from sage.repl.notebook_ipython import have_prerequisites, SageNotebookApp
|
|
+ from sage.repl.ipython_kernel.install import \
|
|
+ SageKernelSpec, have_prerequisites
|
|
+ SageKernelSpec.update()
|
|
if not have_prerequisites():
|
|
print(self.PREREQUISITE_ERROR)
|
|
raise SystemExit(1)
|
|
- app = SageNotebookApp.instance()
|
|
- app.initialize(argv)
|
|
- app.start()
|
|
+ from IPython import start_ipython
|
|
+ start_ipython(['notebook'] + argv)
|
|
|
|
|
|
description = \
|
|
diff --git a/src/doc/en/reference/index.rst b/src/doc/en/reference/index.rst
|
|
index 607121f..44119a8 100644
|
|
--- a/src/doc/en/reference/index.rst
|
|
+++ b/src/doc/en/reference/index.rst
|
|
@@ -10,7 +10,7 @@ Enjoy Sage!
|
|
Table of Contents
|
|
=================
|
|
|
|
-* :doc:`The Sage Command Line <repl/index>`
|
|
+* :doc:`The Sage Command Line (REPL) <repl/index>`
|
|
* :doc:`The Sage Notebook <notebook/index>`
|
|
|
|
Calculus, Plotting
|
|
diff --git a/src/doc/en/reference/repl/index.rst b/src/doc/en/reference/repl/index.rst
|
|
index 5271140..7a98831 100644
|
|
--- a/src/doc/en/reference/repl/index.rst
|
|
+++ b/src/doc/en/reference/repl/index.rst
|
|
@@ -20,7 +20,8 @@ tutorial.
|
|
sage/repl/readline_extra_commands
|
|
sage/repl/interpreter
|
|
sage/repl/ipython_extension
|
|
- sage/repl/notebook_ipython
|
|
+ sage/repl/ipython_kernel/install
|
|
+ sage/repl/ipython_kernel/kernel
|
|
|
|
|
|
Preparsing
|
|
@@ -63,7 +64,26 @@ this works using a modified displayhook in Python.
|
|
sage/repl/display/pretty_print
|
|
sage/repl/display/fancy_repr
|
|
sage/repl/display/util
|
|
- sage/repl/display/python_hook
|
|
|
|
|
|
+Display Backend Infrastructure
|
|
+------------------------------
|
|
+
|
|
+.. toctree::
|
|
+ :maxdepth: 2
|
|
+
|
|
+ sage/repl/rich_output/display_manager
|
|
+ sage/repl/rich_output/preferences
|
|
+ sage/repl/rich_output/buffer
|
|
+ sage/repl/rich_output/output_basic
|
|
+ sage/repl/rich_output/output_graphics
|
|
+ sage/repl/rich_output/output_graphics3d
|
|
+ sage/repl/rich_output/output_catalog
|
|
+
|
|
+ sage/repl/rich_output/backend_base
|
|
+ sage/repl/rich_output/backend_test
|
|
+ sage/repl/rich_output/backend_doctest
|
|
+ sage/repl/rich_output/backend_ipython
|
|
+
|
|
+
|
|
.. include:: ../footer.txt
|
|
diff --git a/src/doc/en/reference/structure/index.rst b/src/doc/en/reference/structure/index.rst
|
|
index 7cb978a..f7149a8 100644
|
|
--- a/src/doc/en/reference/structure/index.rst
|
|
+++ b/src/doc/en/reference/structure/index.rst
|
|
@@ -23,7 +23,6 @@ Basic Structures
|
|
sage/structure/element_wrapper
|
|
sage/structure/indexed_generators
|
|
sage/structure/global_options
|
|
- sage/structure/graphics_file
|
|
|
|
sage/sets/cartesian_product
|
|
sage/sets/family
|
|
diff --git a/src/doc/en/thematic_tutorials/polytutorial.rst b/src/doc/en/thematic_tutorials/polytutorial.rst
|
|
index c731c10..e60370f 100644
|
|
--- a/src/doc/en/thematic_tutorials/polytutorial.rst
|
|
+++ b/src/doc/en/thematic_tutorials/polytutorial.rst
|
|
@@ -36,7 +36,7 @@ Of course, you want to know what this object looks like:
|
|
|
|
::
|
|
|
|
- sage: show(P1)
|
|
+ sage: P1.plot()
|
|
Graphics object consisting of 6 graphics primitives
|
|
|
|
.. end of output
|
|
@@ -70,7 +70,7 @@ translate each inequality into a vector. For example,
|
|
::
|
|
|
|
sage: altP1 = Polyhedron(ieqs=[(12, -4, 1), (26, 1, 7),(5,1,0), (28, 2, -9)])
|
|
- sage: show(altP1)
|
|
+ sage: altP1.plot()
|
|
Graphics object consisting of 6 graphics primitives
|
|
|
|
.. end of output
|
|
@@ -119,7 +119,7 @@ to a rational\-lattice polytope. Let's look at that.
|
|
|
|
::
|
|
|
|
- sage: show(P1dual)
|
|
+ sage: P1dual.plot()
|
|
Graphics object consisting of 6 graphics primitives
|
|
|
|
|
|
@@ -127,7 +127,7 @@ to a rational\-lattice polytope. Let's look at that.
|
|
|
|
::
|
|
|
|
- sage: show(P1)+show(P1dual)
|
|
+ sage: P1.plot() + P1dual.plot()
|
|
Graphics object consisting of 12 graphics primitives
|
|
|
|
|
|
@@ -139,7 +139,7 @@ very different size. Let's rescale.
|
|
|
|
::
|
|
|
|
- sage: show((1/4)*P1)+show(4*P1dual)
|
|
+ sage: ((1/4)*P1).plot() + (4*P1dual).plot()
|
|
Graphics object consisting of 12 graphics primitives
|
|
|
|
.. end of output
|
|
@@ -155,7 +155,7 @@ example that makes the issue a bit clearer.
|
|
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 5 vertices
|
|
sage: P2dual = P2.polar(); P2dual
|
|
A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 5 vertices
|
|
- sage: show(P2)+show(P2dual)
|
|
+ sage: P2.plot() + P2dual.plot()
|
|
Graphics object consisting of 14 graphics primitives
|
|
|
|
.. end of output
|
|
@@ -166,7 +166,7 @@ at this...
|
|
|
|
::
|
|
|
|
- sage: show(P2)+show(-1*P2dual)
|
|
+ sage: P2.plot() + (-1*P2dual).plot()
|
|
Graphics object consisting of 14 graphics primitives
|
|
|
|
.. end of output
|
|
@@ -223,14 +223,14 @@ visualization software such as Javaview and Jmol installed.)
|
|
A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 5 vertices
|
|
sage: P4 = Polyhedron(vertices=[(-1,1,0),(1,1,0),(-1,0,1), (1,0,1),(0,-1,1),(0,1,1)]); P4
|
|
A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 6 vertices
|
|
- sage: show(P3)+show(P4)
|
|
+ sage: P3.plot() + P4.plot()
|
|
Graphics3d Object
|
|
|
|
.. end of output
|
|
|
|
::
|
|
|
|
- sage: show(P3+P4)
|
|
+ sage: (P3+P4).plot()
|
|
Graphics3d Object
|
|
|
|
.. end of output
|
|
@@ -241,14 +241,14 @@ syntaxes!
|
|
|
|
::
|
|
|
|
- sage: int12 = P1.intersection(P2*.5); show(int12)
|
|
+ sage: int12 = P1.intersection(P2*.5); int12.plot()
|
|
Graphics object consisting of 7 graphics primitives
|
|
|
|
.. end of output
|
|
|
|
::
|
|
|
|
- sage: int34 = P3 & P4; show(int34)
|
|
+ sage: int34 = P3 & P4; int34.plot()
|
|
Graphics3d Object
|
|
|
|
.. end of output
|
|
@@ -259,7 +259,7 @@ Should one wish to translate, one can.
|
|
::
|
|
|
|
sage: transP2 = P2.translation([2,1])
|
|
- sage: show(P2)+show(transP2)
|
|
+ sage: P2.plot() + transP2.plot()
|
|
Graphics object consisting of 14 graphics primitives
|
|
|
|
.. end of output
|
|
@@ -269,21 +269,21 @@ Then of course we can take prisms, pyramids, and bipyramids of polytopes...
|
|
|
|
::
|
|
|
|
- sage: show(P2.prism())
|
|
+ sage: P2.prism().plot()
|
|
Graphics3d Object
|
|
|
|
.. end of output
|
|
|
|
::
|
|
|
|
- sage: show(P1.pyramid())
|
|
+ sage: P1.pyramid().plot()
|
|
Graphics3d Object
|
|
|
|
.. end of output
|
|
|
|
::
|
|
|
|
- sage: show(P2dual.bipyramid())
|
|
+ sage: P2dual.bipyramid().plot()
|
|
Graphics3d Object
|
|
|
|
.. end of output
|
|
@@ -308,7 +308,7 @@ Let's look at a 4\-dimensional polytope.
|
|
::
|
|
|
|
sage: P8 = polytopes.n_cube(4)
|
|
- sage: P8.show()
|
|
+ sage: P8.plot()
|
|
Graphics3d Object
|
|
|
|
.. end of output
|
|
diff --git a/src/ext/doctest/invalid/syntax_error.tachyon b/src/ext/doctest/invalid/syntax_error.tachyon
|
|
new file mode 100644
|
|
index 0000000..1cdd7a5
|
|
--- /dev/null
|
|
+++ b/src/ext/doctest/invalid/syntax_error.tachyon
|
|
@@ -0,0 +1,91 @@
|
|
+# This is NOT a valid tachyon input file
|
|
+#
|
|
+# We use it to test that tachyon errors are diagnosed in the Sage
|
|
+# interface to tachyon.
|
|
+
|
|
+
|
|
+begin_scene
|
|
+resolution 400 400
|
|
+
|
|
+ camera
|
|
+ zoom 1.0
|
|
+ aspectratio 1.0
|
|
+ antialiasing 8
|
|
+ raydepth 8
|
|
+ center 2.3 2.4 2.0
|
|
+ viewdir -2.3 -2.4 -2.0
|
|
+ updir 0.0 0.0 1.0
|
|
+ end_camera
|
|
+
|
|
+
|
|
+ light center 4.0 3.0 2.0
|
|
+ rad 0.2
|
|
+ color 1.0 1.0 1.0
|
|
+
|
|
+ plane
|
|
+ center -2000 -1000 -500
|
|
+ normal 2.3 2.4 2.0
|
|
+ TEXTURE
|
|
+ AMBIENT 1.0 DIFFUSE 1.0 SPECULAR 1.0 OPACITY 1.0
|
|
+ COLOR 1.0 1.0 1.0
|
|
+ TEXFUNC 0
|
|
+
|
|
+ Texdef texture239
|
|
+ Ambient 0.333333333333 Diffuse 0.666666666667 Specular 0.0 Opacity 1
|
|
+ Color 0.0 0.0 0.0
|
|
+ TexFunc 0
|
|
+Texdef texture240
|
|
+ Ambient 0.333333333333 Diffuse 0.666666666667 Specular 0.0 Opacity 1/2
|
|
+ Color 0.0 0.0 0.0
|
|
+ TexFunc 0
|
|
+
|
|
+ TRI V0 0.5 -0.5 0.5 V1 -0.5 -0.5 0.5 V2 -0.5 0.5 0.5
|
|
+texture239
|
|
+TRI V0 0.5 -0.5 0.5 V1 -0.5 0.5 0.5 V2 0.5 0.5 0.5
|
|
+texture239
|
|
+TRI V0 0.5 -0.5 0.5 V1 0.5 -0.5 -0.5 V2 -0.5 -0.5 -0.5
|
|
+texture239
|
|
+TRI V0 0.5 -0.5 0.5 V1 -0.5 -0.5 -0.5 V2 -0.5 -0.5 0.5
|
|
+texture239
|
|
+TRI V0 0.5 -0.5 0.5 V1 0.5 0.5 0.5 V2 0.5 0.5 -0.5
|
|
+texture239
|
|
+TRI V0 0.5 -0.5 0.5 V1 0.5 0.5 -0.5 V2 0.5 -0.5 -0.5
|
|
+texture239
|
|
+TRI V0 -0.5 -0.5 -0.5 V1 0.5 -0.5 -0.5 V2 0.5 0.5 -0.5
|
|
+texture239
|
|
+TRI V0 -0.5 -0.5 -0.5 V1 0.5 0.5 -0.5 V2 -0.5 0.5 -0.5
|
|
+texture239
|
|
+TRI V0 0.5 0.5 -0.5 V1 0.5 0.5 0.5 V2 -0.5 0.5 0.5
|
|
+texture239
|
|
+TRI V0 0.5 0.5 -0.5 V1 -0.5 0.5 0.5 V2 -0.5 0.5 -0.5
|
|
+texture239
|
|
+TRI V0 -0.5 0.5 0.5 V1 -0.5 -0.5 0.5 V2 -0.5 -0.5 -0.5
|
|
+texture239
|
|
+TRI V0 -0.5 0.5 0.5 V1 -0.5 -0.5 -0.5 V2 -0.5 0.5 -0.5
|
|
+texture239
|
|
+TRI V0 3 -0.5 0.5 V1 2 -0.5 0.5 V2 2 0.5 0.5
|
|
+texture240
|
|
+TRI V0 3 -0.5 0.5 V1 2 0.5 0.5 V2 3 0.5 0.5
|
|
+texture240
|
|
+TRI V0 3 -0.5 0.5 V1 3 -0.5 -0.5 V2 2 -0.5 -0.5
|
|
+texture240
|
|
+TRI V0 3 -0.5 0.5 V1 2 -0.5 -0.5 V2 2 -0.5 0.5
|
|
+texture240
|
|
+TRI V0 3 -0.5 0.5 V1 3 0.5 0.5 V2 3 0.5 -0.5
|
|
+texture240
|
|
+TRI V0 3 -0.5 0.5 V1 3 0.5 -0.5 V2 3 -0.5 -0.5
|
|
+texture240
|
|
+TRI V0 2 -0.5 -0.5 V1 3 -0.5 -0.5 V2 3 0.5 -0.5
|
|
+texture240
|
|
+TRI V0 2 -0.5 -0.5 V1 3 0.5 -0.5 V2 2 0.5 -0.5
|
|
+texture240
|
|
+TRI V0 3 0.5 -0.5 V1 3 0.5 0.5 V2 2 0.5 0.5
|
|
+texture240
|
|
+TRI V0 3 0.5 -0.5 V1 2 0.5 0.5 V2 2 0.5 -0.5
|
|
+texture240
|
|
+TRI V0 2 0.5 0.5 V1 2 -0.5 0.5 V2 2 -0.5 -0.5
|
|
+texture240
|
|
+TRI V0 2 0.5 0.5 V1 2 -0.5 -0.5 V2 2 0.5 -0.5
|
|
+texture240
|
|
+
|
|
+end_scene
|
|
\ No newline at end of file
|
|
diff --git a/src/ext/doctest/rich_output/example.canvas3d b/src/ext/doctest/rich_output/example.canvas3d
|
|
new file mode 100644
|
|
index 0000000..43b93e9
|
|
--- /dev/null
|
|
+++ b/src/ext/doctest/rich_output/example.canvas3d
|
|
@@ -0,0 +1 @@
|
|
+[{vertices:[{x:1,y:1,z:1},{x:-1,y:1,z:1},{x:-1,y:-1,z:1},{x:1,y:-1,z:1}],faces:[[0,1,2,3]],color:'008000'},{vertices:[{x:1,y:1,z:1},{x:-1,y:1,z:1},{x:-1,y:1,z:-1},{x:1,y:1,z:-1}],faces:[[0,1,2,3]],color:'008000'},{vertices:[{x:1,y:1,z:1},{x:1,y:-1,z:1},{x:1,y:-1,z:-1},{x:1,y:1,z:-1}],faces:[[0,1,2,3]],color:'008000'},{vertices:[{x:-1,y:1,z:1},{x:-1,y:-1,z:1},{x:-1,y:-1,z:-1},{x:-1,y:1,z:-1}],faces:[[0,1,2,3]],color:'008000'},{vertices:[{x:1,y:1,z:-1},{x:-1,y:1,z:-1},{x:-1,y:-1,z:-1},{x:1,y:-1,z:-1}],faces:[[0,1,2,3]],color:'008000'},{vertices:[{x:1,y:-1,z:1},{x:-1,y:-1,z:1},{x:-1,y:-1,z:-1},{x:1,y:-1,z:-1}],faces:[[0,1,2,3]],color:'008000'}]
|
|
\ No newline at end of file
|
|
diff --git a/src/ext/doctest/rich_output/example.dvi b/src/ext/doctest/rich_output/example.dvi
|
|
new file mode 100644
|
|
index 0000000..6c2f385
|
|
--- /dev/null
|
|
+++ b/src/ext/doctest/rich_output/example.dvi
|
|
Binary files differ
|
|
diff --git a/src/ext/doctest/rich_output/example.gif b/src/ext/doctest/rich_output/example.gif
|
|
new file mode 100644
|
|
index 0000000..12b3275
|
|
--- /dev/null
|
|
+++ b/src/ext/doctest/rich_output/example.gif
|
|
Binary files differ
|
|
diff --git a/src/ext/doctest/rich_output/example.jpg b/src/ext/doctest/rich_output/example.jpg
|
|
new file mode 100644
|
|
index 0000000..5e4439d
|
|
--- /dev/null
|
|
+++ b/src/ext/doctest/rich_output/example.jpg
|
|
Binary files differ
|
|
diff --git a/src/ext/doctest/rich_output/example.pdf b/src/ext/doctest/rich_output/example.pdf
|
|
new file mode 100644
|
|
index 0000000..e47041b
|
|
--- /dev/null
|
|
+++ b/src/ext/doctest/rich_output/example.pdf
|
|
Binary files differ
|
|
diff --git a/src/ext/doctest/rich_output/example.png b/src/ext/doctest/rich_output/example.png
|
|
new file mode 100644
|
|
index 0000000..3b8e082
|
|
--- /dev/null
|
|
+++ b/src/ext/doctest/rich_output/example.png
|
|
Binary files differ
|
|
diff --git a/src/ext/doctest/rich_output/example.svg b/src/ext/doctest/rich_output/example.svg
|
|
new file mode 100644
|
|
index 0000000..f736828
|
|
--- /dev/null
|
|
+++ b/src/ext/doctest/rich_output/example.svg
|
|
@@ -0,0 +1,54 @@
|
|
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
|
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
|
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
+<!-- Created with matplotlib (http://matplotlib.org/) -->
|
|
+<svg height="420pt" version="1.1" viewBox="0 0 420 420" width="420pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
+ <defs>
|
|
+ <style type="text/css">
|
|
+*{stroke-linecap:butt;stroke-linejoin:round;}
|
|
+ </style>
|
|
+ </defs>
|
|
+ <g id="figure_1">
|
|
+ <g id="patch_1">
|
|
+ <path d="
|
|
+M0 420.48
|
|
+L420.48 420.48
|
|
+L420.48 0
|
|
+L0 0
|
|
+z
|
|
+" style="fill:#ffffff;"/>
|
|
+ </g>
|
|
+ <g id="axes_1">
|
|
+ <g id="patch_2">
|
|
+ <path d="
|
|
+M7.2 413.28
|
|
+L413.28 413.28
|
|
+L413.28 7.2
|
|
+L7.2 7.2
|
|
+z
|
|
+" style="fill:#ffffff;"/>
|
|
+ </g>
|
|
+ <g id="matplotlib.axis_1"/>
|
|
+ <g id="matplotlib.axis_2"/>
|
|
+ <g id="patch_3">
|
|
+ <path clip-path="url(#p85c0e9ffdb)" d="
|
|
+M210.24 405.471
|
|
+C262.016 405.471 311.678 384.9 348.289 348.289
|
|
+C384.9 311.678 405.471 262.016 405.471 210.24
|
|
+C405.471 158.464 384.9 108.802 348.289 72.191
|
|
+C311.678 35.58 262.016 15.0092 210.24 15.0092
|
|
+C158.464 15.0092 108.802 35.58 72.191 72.191
|
|
+C35.58 108.802 15.0092 158.464 15.0092 210.24
|
|
+C15.0092 262.016 35.58 311.678 72.191 348.289
|
|
+C108.802 384.9 158.464 405.471 210.24 405.471
|
|
+z
|
|
+" style="fill:none;stroke:#0000ff;"/>
|
|
+ </g>
|
|
+ </g>
|
|
+ </g>
|
|
+ <defs>
|
|
+ <clipPath id="p85c0e9ffdb">
|
|
+ <rect height="406.08" width="406.08" x="7.2" y="7.2"/>
|
|
+ </clipPath>
|
|
+ </defs>
|
|
+</svg>
|
|
diff --git a/src/ext/doctest/rich_output/example_jmol.spt.zip b/src/ext/doctest/rich_output/example_jmol.spt.zip
|
|
new file mode 100644
|
|
index 0000000..f9748e1
|
|
--- /dev/null
|
|
+++ b/src/ext/doctest/rich_output/example_jmol.spt.zip
|
|
Binary files differ
|
|
diff --git a/src/ext/doctest/rich_output/example_wavefront/scene.mtl b/src/ext/doctest/rich_output/example_wavefront/scene.mtl
|
|
new file mode 100644
|
|
index 0000000..a2b7d31
|
|
--- /dev/null
|
|
+++ b/src/ext/doctest/rich_output/example_wavefront/scene.mtl
|
|
@@ -0,0 +1,7 @@
|
|
+newmtl texture177
|
|
+Ka 0.2 0.2 0.5
|
|
+Kd 0.4 0.4 1.0
|
|
+Ks 0.0 0.0 0.0
|
|
+illum 1
|
|
+Ns 1
|
|
+d 1
|
|
diff --git a/src/ext/doctest/rich_output/example_wavefront/scene.obj b/src/ext/doctest/rich_output/example_wavefront/scene.obj
|
|
new file mode 100644
|
|
index 0000000..1ece4f1
|
|
--- /dev/null
|
|
+++ b/src/ext/doctest/rich_output/example_wavefront/scene.obj
|
|
@@ -0,0 +1,17 @@
|
|
+mtllib scene.mtl
|
|
+g obj_1
|
|
+usemtl texture177
|
|
+v 0.5 0.5 0.5
|
|
+v -0.5 0.5 0.5
|
|
+v -0.5 -0.5 0.5
|
|
+v 0.5 -0.5 0.5
|
|
+v 0.5 0.5 -0.5
|
|
+v -0.5 0.5 -0.5
|
|
+v 0.5 -0.5 -0.5
|
|
+v -0.5 -0.5 -0.5
|
|
+f 1 2 3 4
|
|
+f 1 5 6 2
|
|
+f 1 4 7 5
|
|
+f 6 5 7 8
|
|
+f 7 4 3 8
|
|
+f 3 2 6 8
|
|
diff --git a/src/ext/notebook-ipython/logo-64x64.png b/src/ext/notebook-ipython/logo-64x64.png
|
|
new file mode 100644
|
|
index 0000000..4b6da1f
|
|
--- /dev/null
|
|
+++ b/src/ext/notebook-ipython/logo-64x64.png
|
|
Binary files differ
|
|
diff --git a/src/ext/notebook-ipython/logo.svg b/src/ext/notebook-ipython/logo.svg
|
|
new file mode 100644
|
|
index 0000000..7d85809
|
|
--- /dev/null
|
|
+++ b/src/ext/notebook-ipython/logo.svg
|
|
@@ -0,0 +1,352 @@
|
|
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
+
|
|
+<svg
|
|
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
+ xmlns:cc="http://creativecommons.org/ns#"
|
|
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
+ xmlns:svg="http://www.w3.org/2000/svg"
|
|
+ xmlns="http://www.w3.org/2000/svg"
|
|
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
+ width="100"
|
|
+ height="100"
|
|
+ viewBox="0 0 99.999997 99.999997"
|
|
+ id="svg5182"
|
|
+ version="1.1"
|
|
+ inkscape:version="0.91 r13725"
|
|
+ sodipodi:docname="sage-logo.svg">
|
|
+ <defs
|
|
+ id="defs5184" />
|
|
+ <sodipodi:namedview
|
|
+ id="base"
|
|
+ pagecolor="#ffffff"
|
|
+ bordercolor="#666666"
|
|
+ borderopacity="1.0"
|
|
+ inkscape:pageopacity="0.0"
|
|
+ inkscape:pageshadow="2"
|
|
+ inkscape:zoom="2.8"
|
|
+ inkscape:cx="4.5104621"
|
|
+ inkscape:cy="-33.017238"
|
|
+ inkscape:document-units="px"
|
|
+ inkscape:current-layer="layer1"
|
|
+ showgrid="false"
|
|
+ fit-margin-top="0"
|
|
+ fit-margin-left="0"
|
|
+ fit-margin-right="0"
|
|
+ fit-margin-bottom="0"
|
|
+ units="px"
|
|
+ inkscape:window-width="2560"
|
|
+ inkscape:window-height="1534"
|
|
+ inkscape:window-x="0"
|
|
+ inkscape:window-y="27"
|
|
+ inkscape:window-maximized="1" />
|
|
+ <metadata
|
|
+ id="metadata5187">
|
|
+ <rdf:RDF>
|
|
+ <cc:Work
|
|
+ rdf:about="">
|
|
+ <dc:format>image/svg+xml</dc:format>
|
|
+ <dc:type
|
|
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
+ <dc:title></dc:title>
|
|
+ </cc:Work>
|
|
+ </rdf:RDF>
|
|
+ </metadata>
|
|
+ <g
|
|
+ inkscape:label="Layer 1"
|
|
+ inkscape:groupmode="layer"
|
|
+ id="layer1"
|
|
+ transform="translate(-184.28571,-313.79078)">
|
|
+ <g
|
|
+ transform="translate(-817.28326,-402.32598)"
|
|
+ id="g12802"
|
|
+ inkscape:export-filename="/home/sirius/Images/SAGE Logos/icons_strip.png"
|
|
+ inkscape:export-xdpi="90"
|
|
+ inkscape:export-ydpi="90">
|
|
+ <rect
|
|
+ inkscape:export-ydpi="90"
|
|
+ inkscape:export-xdpi="90"
|
|
+ inkscape:export-filename="/home/sirius/Images/SAGE Logos/sage_webpage_icons.png"
|
|
+ ry="11.531985"
|
|
+ rx="11.531984"
|
|
+ y="716.11676"
|
|
+ x="1001.569"
|
|
+ height="100"
|
|
+ width="100"
|
|
+ id="rect12804"
|
|
+ style="opacity:0.98999999;fill:#9393ff;fill-opacity:0.99216464;stroke:none;stroke-width:0.80000001;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.98431684" />
|
|
+ <g
|
|
+ inkscape:export-ydpi="90"
|
|
+ inkscape:export-xdpi="90"
|
|
+ inkscape:export-filename="/home/sirius/Images/SAGE Logos/sage_webpage_icons.png"
|
|
+ transform="matrix(0.4795482,0,0,0.4795482,301.82045,-349.76993)"
|
|
+ id="g12806">
|
|
+ <circle
|
|
+ r="7.3256478"
|
|
+ cy="1792.4095"
|
|
+ cx="1324.929"
|
|
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
+ id="path12808"
|
|
+ transform="matrix(0.9672595,0,0,0.9672595,235.69886,548.04605)" />
|
|
+ <circle
|
|
+ r="7.3256478"
|
|
+ cy="1792.4095"
|
|
+ cx="1324.929"
|
|
+ transform="matrix(0.9672595,0,0,0.9672595,308.74604,539.35166)"
|
|
+ id="path12810"
|
|
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
|
+ <circle
|
|
+ r="7.3256478"
|
|
+ cy="1792.4095"
|
|
+ cx="1324.929"
|
|
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
+ id="path12812"
|
|
+ transform="matrix(1.2053547,0,0,1.2053547,-25.196551,182.19849)" />
|
|
+ <circle
|
|
+ r="7.3256478"
|
|
+ cy="1792.4095"
|
|
+ cx="1324.929"
|
|
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
+ id="path12814"
|
|
+ transform="matrix(0.7953719,0,0,0.7953719,449.34462,925.45441)" />
|
|
+ <circle
|
|
+ r="7.3256478"
|
|
+ cy="1792.4095"
|
|
+ cx="1324.929"
|
|
+ transform="matrix(0.7953719,0,0,0.7953719,499.30379,973.24771)"
|
|
+ id="path12816"
|
|
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
|
+ <circle
|
|
+ r="7.3256478"
|
|
+ cy="1792.4095"
|
|
+ cx="1324.929"
|
|
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
+ id="path12818"
|
|
+ transform="matrix(0.7953719,0,0,0.7953719,556.29908,956.69427)" />
|
|
+ <circle
|
|
+ r="7.3256478"
|
|
+ cy="1792.4095"
|
|
+ cx="1324.929"
|
|
+ transform="matrix(0.7953719,0,0,0.7953719,581.00221,902.90894)"
|
|
+ id="path12820"
|
|
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
|
+ <circle
|
|
+ r="7.3256478"
|
|
+ cy="1792.4095"
|
|
+ cx="1324.929"
|
|
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
+ id="path12822"
|
|
+ transform="matrix(0.6234843,0,0,0.6234843,800.65848,1180.6657)" />
|
|
+ <circle
|
|
+ r="7.3256478"
|
|
+ cy="1792.4095"
|
|
+ cx="1324.929"
|
|
+ transform="matrix(0.6234843,0,0,0.6234843,766.33259,1139.9519)"
|
|
+ id="path12824"
|
|
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
|
+ <circle
|
|
+ r="7.3256478"
|
|
+ cy="1792.4095"
|
|
+ cx="1324.929"
|
|
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
+ id="path12826"
|
|
+ transform="matrix(0.6234843,0,0,0.6234843,664.75027,1230.1938)" />
|
|
+ <circle
|
|
+ r="7.3256478"
|
|
+ cy="1792.4095"
|
|
+ cx="1324.929"
|
|
+ transform="matrix(0.6234843,0,0,0.6234843,701.58781,1267.5497)"
|
|
+ id="path12828"
|
|
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
|
+ <circle
|
|
+ r="7.3256478"
|
|
+ cy="1792.4095"
|
|
+ cx="1324.929"
|
|
+ transform="matrix(0.7953719,0,0,0.7953719,440.41431,869.81028)"
|
|
+ id="path12830"
|
|
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
|
+ <circle
|
|
+ r="7.3256478"
|
|
+ cy="1792.4095"
|
|
+ cx="1324.929"
|
|
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
+ id="path12832"
|
|
+ transform="matrix(0.7953719,0,0,0.7953719,484.50773,829.39633)" />
|
|
+ <circle
|
|
+ r="7.3256478"
|
|
+ cy="1792.4095"
|
|
+ cx="1324.929"
|
|
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
+ id="path12834"
|
|
+ transform="matrix(0.6234843,0,0,0.6234843,699.00639,1196.6154)" />
|
|
+ <circle
|
|
+ r="7.3256478"
|
|
+ cy="1792.4095"
|
|
+ cx="1324.929"
|
|
+ transform="matrix(0.6234843,0,0,0.6234843,749.0301,1256.3969)"
|
|
+ id="path12836"
|
|
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1590.0065,2271.6035 -18.6281,72.6731"
|
|
+ id="path12838" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1589.0995,2273.5223 -72.5588,8.4545"
|
|
+ id="path12840" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1571.3087,2343.4372 -54.6982,-61.8801"
|
|
+ id="path12842" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1571.3087,2343.8569 -18.07,55.944"
|
|
+ id="path12844" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1516.4012,2281.3172 21.0002,-24.3443"
|
|
+ id="path12846" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1493.9359,2295.4681 21.7676,-14.3307"
|
|
+ id="path12848" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1503.0057,2350.6326 13.3955,-69.9749"
|
|
+ id="path12850" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1552.1922,2398.5417 -49.5353,-46.59"
|
|
+ id="path12852" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1610.0997,2381.3328 -58.1168,16.969"
|
|
+ id="path12854" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1634.2395,2328.3869 -24.1398,53.1257"
|
|
+ id="path12856" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1570.5412,2343.4372 62.8611,-14.6306"
|
|
+ id="path12858" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1590.1461,2273.0426 43.1166,55.1644"
|
|
+ id="path12860" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1501.7987,2350.6326 66.9774,-7.1954"
|
|
+ id="path12862" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1551.4038,2398.062 -24.4188,-13.1316"
|
|
+ id="path12864" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1489.38,2347.2747 36.3492,37.296"
|
|
+ id="path12866" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1493.2173,2295.9478 -2.721,51.3269"
|
|
+ id="path12868" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1537.3805,2254.8743 54.0702,1.8588"
|
|
+ id="path12870" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1627.172,2298.5261 -34.2562,-41.9729"
|
|
+ id="path12872" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1635.4744,2328.207 -9.0001,-30.1006"
|
|
+ id="path12874" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1589.4972,2272.2631 3.2093,-15.2902"
|
|
+ id="path12876" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1503.6825,2351.2305 -14.1629,-2.6983"
|
|
+ id="path12878" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1526.985,2385.4084 46.7446,-11.4526"
|
|
+ id="path12880" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1574.9157,2373.8958 33.6282,7.2554"
|
|
+ id="path12882" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1492.5121,2295.941 61.203,-6.8478"
|
|
+ id="path12884" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1553.7151,2287.7387 36.4241,-13.5452"
|
|
+ id="path12886" />
|
|
+ <circle
|
|
+ r="7.3256478"
|
|
+ cy="1792.4095"
|
|
+ cx="1324.929"
|
|
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
+ id="path12888"
|
|
+ transform="matrix(0.6234843,0,0,0.6234843,726.86911,1170.9368)" />
|
|
+ <circle
|
|
+ r="7.3256478"
|
|
+ cy="1792.4095"
|
|
+ cx="1324.929"
|
|
+ transform="matrix(0.5833906,0,0,0.5833906,837.2333,1298.2147)"
|
|
+ id="path12890"
|
|
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1609.6576,2343.739 16.404,-46.0639"
|
|
+ id="path12892" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
+ d="m 1609.4298,2381.481 0.5696,-38.77"
|
|
+ id="path12894" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ id="path12896"
|
|
+ d="m 1502.1919,2350.0723 23.4339,-35.9573"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
+ <path
|
|
+ inkscape:connector-curvature="0"
|
|
+ id="path12898"
|
|
+ d="M 1537.8572,2255.3651 1524.94,2314.115"
|
|
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
+ </g>
|
|
+ </g>
|
|
+ </g>
|
|
+</svg>
|
|
diff --git a/src/ext/notebook-ipython/templates/404.html b/src/ext/notebook-ipython/templates/404.html
|
|
deleted file mode 100644
|
|
index 7335051..0000000
|
|
--- a/src/ext/notebook-ipython/templates/404.html
|
|
+++ /dev/null
|
|
@@ -1,5 +0,0 @@
|
|
-{% extends "error.html" %}
|
|
-{% block error_detail %}
|
|
-<p>You are requesting a page that does not exist!</p>
|
|
-{% endblock %}
|
|
-
|
|
diff --git a/src/ext/notebook-ipython/templates/error.html b/src/ext/notebook-ipython/templates/error.html
|
|
deleted file mode 100644
|
|
index bedf06c..0000000
|
|
--- a/src/ext/notebook-ipython/templates/error.html
|
|
+++ /dev/null
|
|
@@ -1,31 +0,0 @@
|
|
-{% extends "page.html" %}
|
|
-
|
|
-{% block login_widget %}
|
|
-{% endblock %}
|
|
-
|
|
-{% block stylesheet %}
|
|
-{{super()}}
|
|
-<style type="text/css">
|
|
-/* disable initial hide */
|
|
-div#header, div#site {
|
|
- display: block;
|
|
-}
|
|
-</style>
|
|
-{% endblock %}
|
|
-{% block site %}
|
|
-
|
|
-<div class="error">
|
|
- {% block h1_error %}
|
|
- <h1>{{status_code}} : {{status_message}}</h1>
|
|
- {% endblock h1_error %}
|
|
- {% block error_detail %}
|
|
- {% if message %}
|
|
- <p>The error was:</p>
|
|
- <div class="traceback-wrapper">
|
|
- <pre class="traceback">{{message}}</pre>
|
|
- </div>
|
|
- {% endif %}
|
|
- {% endblock %}
|
|
-</header>
|
|
-
|
|
-{% endblock %}
|
|
diff --git a/src/ext/notebook-ipython/templates/login.html b/src/ext/notebook-ipython/templates/login.html
|
|
deleted file mode 100644
|
|
index 7244bee..0000000
|
|
--- a/src/ext/notebook-ipython/templates/login.html
|
|
+++ /dev/null
|
|
@@ -1,52 +0,0 @@
|
|
-{% extends "page.html" %}
|
|
-
|
|
-
|
|
-{% block stylesheet %}
|
|
-{{super()}}
|
|
-<link rel="stylesheet" href="{{ static_url("auth/css/override.css") }}" type="text/css" />
|
|
-{% endblock %}
|
|
-
|
|
-{% block login_widget %}
|
|
-{% endblock %}
|
|
-
|
|
-{% block site %}
|
|
-
|
|
-<div id="ipython-main-app" class="container">
|
|
-
|
|
- {% if login_available %}
|
|
- <div class="row">
|
|
- <div class="navbar span8 offset2">
|
|
- <div class="navbar-inner">
|
|
- <div class="container">
|
|
- <div class="center-nav">
|
|
- <p class="navbar-text nav">Password:</p>
|
|
- <form action="{{base_url}}login?next={{next}}" method="post" class="navbar-form pull-left">
|
|
- <input type="password" name="password" id="password_input">
|
|
- <button type="submit" id="login_submit">Log in</button>
|
|
- </form>
|
|
- </div>
|
|
- </div>
|
|
- </div>
|
|
- </div>
|
|
- </div>
|
|
- {% endif %}
|
|
- {% if message %}
|
|
- <div class="row">
|
|
- {% for key in message %}
|
|
- <div class="message {{key}}">
|
|
- {{message[key]}}
|
|
- </div>
|
|
- {% endfor %}
|
|
- </div>
|
|
- {% endif %}
|
|
-
|
|
-<div/>
|
|
-
|
|
-{% endblock %}
|
|
-
|
|
-
|
|
-{% block script %}
|
|
-
|
|
-<script src="{{static_url("auth/js/loginmain.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-
|
|
-{% endblock %}
|
|
diff --git a/src/ext/notebook-ipython/templates/logout.html b/src/ext/notebook-ipython/templates/logout.html
|
|
deleted file mode 100644
|
|
index 35c65c5..0000000
|
|
--- a/src/ext/notebook-ipython/templates/logout.html
|
|
+++ /dev/null
|
|
@@ -1,38 +0,0 @@
|
|
-{% extends "page.html" %}
|
|
-
|
|
-{% block stylesheet %}
|
|
-{{super()}}
|
|
-<link rel="stylesheet" href="{{ static_url("auth/css/override.css") }}" type="text/css" />
|
|
-{% endblock %}
|
|
-
|
|
-{% block login_widget %}
|
|
-{% endblock %}
|
|
-
|
|
-{% block site %}
|
|
-
|
|
-<div id="ipython-main-app" class="container">
|
|
-
|
|
- {% if message %}
|
|
- {% for key in message %}
|
|
- <div class="message {{key}}">
|
|
- {{message[key]}}
|
|
- </div>
|
|
- {% endfor %}
|
|
- {% endif %}
|
|
-
|
|
- {% if not login_available %}
|
|
- Proceed to the <a href="{{base_url}}">dashboard</a>.
|
|
- {% else %}
|
|
- Proceed to the <a href="{{base_url}}login">login page</a>.
|
|
- {% endif %}
|
|
-
|
|
-
|
|
-<div/>
|
|
-
|
|
-{% endblock %}
|
|
-
|
|
-{% block script %}
|
|
-
|
|
-<script src="{{static_url("auth/js/logoutmain.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-
|
|
-{% endblock %}
|
|
diff --git a/src/ext/notebook-ipython/templates/notebook.html b/src/ext/notebook-ipython/templates/notebook.html
|
|
deleted file mode 100644
|
|
index 8a4377b..0000000
|
|
--- a/src/ext/notebook-ipython/templates/notebook.html
|
|
+++ /dev/null
|
|
@@ -1,371 +0,0 @@
|
|
-{% extends "page.html" %}
|
|
-
|
|
-{% block stylesheet %}
|
|
-
|
|
-{% if mathjax_url %}
|
|
-<script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script>
|
|
-{% endif %}
|
|
-<script type="text/javascript">
|
|
-// MathJax disabled, set as null to distingish from *missing* MathJax,
|
|
-// where it will be undefined, and should prompt a dialog later.
|
|
-window.mathjax_url = "{{mathjax_url}}";
|
|
-</script>
|
|
-
|
|
-<link rel="stylesheet" href="{{ static_url("components/bootstrap-tour/build/css/bootstrap-tour.min.css") }}" type="text/css" />
|
|
-<link rel="stylesheet" href="{{ static_url("components/codemirror/lib/codemirror.css") }}">
|
|
-
|
|
-{{super()}}
|
|
-
|
|
-<link rel="stylesheet" href="{{ static_url("notebook/css/override.css") }}" type="text/css" />
|
|
-
|
|
-{% endblock %}
|
|
-
|
|
-{% block params %}
|
|
-
|
|
-data-project="{{project}}"
|
|
-data-base-url="{{base_url}}"
|
|
-data-notebook-name="{{notebook_name}}"
|
|
-data-notebook-path="{{notebook_path}}"
|
|
-class="notebook_app"
|
|
-
|
|
-{% endblock %}
|
|
-
|
|
-
|
|
-{% block header %}
|
|
-
|
|
-<span id="save_widget" class="nav pull-left">
|
|
- <span id="notebook_name"></span>
|
|
- <span id="checkpoint_status"></span>
|
|
- <span id="autosave_status"></span>
|
|
-</span>
|
|
-
|
|
-{% endblock %}
|
|
-
|
|
-
|
|
-{% block site %}
|
|
-
|
|
-<div id="menubar-container" class="container">
|
|
-<div id="menubar">
|
|
-<div class="navbar">
|
|
- <div class="navbar-inner">
|
|
- <div class="container">
|
|
- <ul id="menus" class="nav">
|
|
- <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
|
|
- <ul id="file_menu" class="dropdown-menu">
|
|
- <li id="new_notebook"
|
|
- title="Make a new notebook (Opens a new window)">
|
|
- <a href="#">New</a></li>
|
|
- <li id="open_notebook"
|
|
- title="Opens a new window with the Dashboard view">
|
|
- <a href="#">Open...</a></li>
|
|
- <!-- <hr/> -->
|
|
- <li class="divider"></li>
|
|
- <li id="copy_notebook"
|
|
- title="Open a copy of this notebook's contents and start a new kernel">
|
|
- <a href="#">Make a Copy...</a></li>
|
|
- <li id="rename_notebook"><a href="#">Rename...</a></li>
|
|
- <li id="save_checkpoint"><a href="#">Save and Checkpoint</a></li>
|
|
- <!-- <hr/> -->
|
|
- <li class="divider"></li>
|
|
- <li id="restore_checkpoint" class="dropdown-submenu"><a href="#">Revert to Checkpoint</a>
|
|
- <ul class="dropdown-menu">
|
|
- <li><a href="#"></a></li>
|
|
- <li><a href="#"></a></li>
|
|
- <li><a href="#"></a></li>
|
|
- <li><a href="#"></a></li>
|
|
- <li><a href="#"></a></li>
|
|
- </ul>
|
|
- </li>
|
|
- <li class="divider"></li>
|
|
- <li id="print_preview"><a href="#">Print Preview</a></li>
|
|
- <li class="dropdown-submenu"><a href="#">Download as</a>
|
|
- <ul class="dropdown-menu">
|
|
- <li id="download_ipynb"><a href="#">IPython Notebook (.ipynb)</a></li>
|
|
- <li id="download_py"><a href="#">Python (.py)</a></li>
|
|
- <li id="download_html"><a href="#">HTML (.html)</a></li>
|
|
- <li id="download_rst"><a href="#">reST (.rst)</a></li>
|
|
- </ul>
|
|
- </li>
|
|
- <li class="divider"></li>
|
|
- <li id="trust_notebook"
|
|
- title="Trust the output of this notebook">
|
|
- <a href="#" >Trust Notebook</a></li>
|
|
- <li class="divider"></li>
|
|
- <li id="kill_and_exit"
|
|
- title="Shutdown this notebook's kernel, and close this window">
|
|
- <a href="#" >Close and halt</a></li>
|
|
- </ul>
|
|
- </li>
|
|
- <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit</a>
|
|
- <ul id="edit_menu" class="dropdown-menu">
|
|
- <li id="cut_cell"><a href="#">Cut Cell</a></li>
|
|
- <li id="copy_cell"><a href="#">Copy Cell</a></li>
|
|
- <li id="paste_cell_above" class="disabled"><a href="#">Paste Cell Above</a></li>
|
|
- <li id="paste_cell_below" class="disabled"><a href="#">Paste Cell Below</a></li>
|
|
- <li id="paste_cell_replace" class="disabled"><a href="#">Paste Cell & Replace</a></li>
|
|
- <li id="delete_cell"><a href="#">Delete Cell</a></li>
|
|
- <li id="undelete_cell" class="disabled"><a href="#">Undo Delete Cell</a></li>
|
|
- <li class="divider"></li>
|
|
- <li id="split_cell"><a href="#">Split Cell</a></li>
|
|
- <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
|
|
- <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
|
|
- <li class="divider"></li>
|
|
- <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
|
|
- <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
|
|
- <li class="divider"></li>
|
|
- <li id="edit_nb_metadata"><a href="#">Edit Notebook Metadata</a></li>
|
|
- </ul>
|
|
- </li>
|
|
- <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">View</a>
|
|
- <ul id="view_menu" class="dropdown-menu">
|
|
- <li id="toggle_header"
|
|
- title="Show/Hide the IPython Notebook logo and notebook title (above menu bar)">
|
|
- <a href="#">Toggle Header</a></li>
|
|
- <li id="toggle_toolbar"
|
|
- title="Show/Hide the action icons (below menu bar)">
|
|
- <a href="#">Toggle Toolbar</a></li>
|
|
- </ul>
|
|
- </li>
|
|
- <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Insert</a>
|
|
- <ul id="insert_menu" class="dropdown-menu">
|
|
- <li id="insert_cell_above"
|
|
- title="Insert an empty Code cell above the currently active cell">
|
|
- <a href="#">Insert Cell Above</a></li>
|
|
- <li id="insert_cell_below"
|
|
- title="Insert an empty Code cell below the currently active cell">
|
|
- <a href="#">Insert Cell Below</a></li>
|
|
- </ul>
|
|
- </li>
|
|
- <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Cell</a>
|
|
- <ul id="cell_menu" class="dropdown-menu">
|
|
- <li id="run_cell" title="Run this cell, and move cursor to the next one">
|
|
- <a href="#">Run</a></li>
|
|
- <li id="run_cell_select_below" title="Run this cell, select below">
|
|
- <a href="#">Run and Select Below</a></li>
|
|
- <li id="run_cell_insert_below" title="Run this cell, insert below">
|
|
- <a href="#">Run and Insert Below</a></li>
|
|
- <li id="run_all_cells" title="Run all cells in the notebook">
|
|
- <a href="#">Run All</a></li>
|
|
- <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
|
|
- <a href="#">Run All Above</a></li>
|
|
- <li id="run_all_cells_below" title="Run this cell and all cells below it">
|
|
- <a href="#">Run All Below</a></li>
|
|
- <li class="divider"></li>
|
|
- <li id="change_cell_type" class="dropdown-submenu"
|
|
- title="All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells">
|
|
- <a href="#">Cell Type</a>
|
|
- <ul class="dropdown-menu">
|
|
- <li id="to_code"
|
|
- title="Contents will be sent to the kernel for execution, and output will display in the footer of cell">
|
|
- <a href="#">Code</a></li>
|
|
- <li id="to_markdown"
|
|
- title="Contents will be rendered as HTML and serve as explanatory text">
|
|
- <a href="#">Markdown</a></li>
|
|
- <li id="to_raw"
|
|
- title="Contents will pass through nbconvert unmodified">
|
|
- <a href="#">Raw NBConvert</a></li>
|
|
- <li id="to_heading1"><a href="#">Heading 1</a></li>
|
|
- <li id="to_heading2"><a href="#">Heading 2</a></li>
|
|
- <li id="to_heading3"><a href="#">Heading 3</a></li>
|
|
- <li id="to_heading4"><a href="#">Heading 4</a></li>
|
|
- <li id="to_heading5"><a href="#">Heading 5</a></li>
|
|
- <li id="to_heading6"><a href="#">Heading 6</a></li>
|
|
- </ul>
|
|
- </li>
|
|
- <li class="divider"></li>
|
|
- <li id="current_outputs" class="dropdown-submenu"><a href="#">Current Output</a>
|
|
- <ul class="dropdown-menu">
|
|
- <li id="toggle_current_output"
|
|
- title="Hide/Show the output of the current cell">
|
|
- <a href="#">Toggle</a>
|
|
- </li>
|
|
- <li id="toggle_current_output_scroll"
|
|
- title="Scroll the output of the current cell">
|
|
- <a href="#">Toggle Scrolling</a>
|
|
- </li>
|
|
- <li id="clear_current_output"
|
|
- title="Clear the output of the current cell">
|
|
- <a href="#">Clear</a>
|
|
- </li>
|
|
- </ul>
|
|
- </li>
|
|
- <li id="all_outputs" class="dropdown-submenu"><a href="#">All Output</a>
|
|
- <ul class="dropdown-menu">
|
|
- <li id="toggle_all_output"
|
|
- title="Hide/Show the output of all cells">
|
|
- <a href="#">Toggle</a>
|
|
- </li>
|
|
- <li id="toggle_all_output_scroll"
|
|
- title="Scroll the output of all cells">
|
|
- <a href="#">Toggle Scrolling</a>
|
|
- </li>
|
|
- <li id="clear_all_output"
|
|
- title="Clear the output of all cells">
|
|
- <a href="#">Clear</a>
|
|
- </li>
|
|
- </ul>
|
|
- </li>
|
|
- </ul>
|
|
- </li>
|
|
- <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Kernel</a>
|
|
- <ul id="kernel_menu" class="dropdown-menu">
|
|
- <li id="int_kernel"
|
|
- title="Send KeyboardInterrupt (CTRL-C) to the Kernel">
|
|
- <a href="#">Interrupt</a></li>
|
|
- <li id="restart_kernel"
|
|
- title="Restart the Kernel">
|
|
- <a href="#">Restart</a></li>
|
|
- </ul>
|
|
- </li>
|
|
- <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
|
|
- <ul id="help_menu" class="dropdown-menu">
|
|
- <li id="notebook_tour" title="A quick tour of the notebook user interface"><a href="#">User Interface Tour</a></li>
|
|
- <li id="keyboard_shortcuts" title="Opens a tooltip with all keyboard shortcuts"><a href="#">Keyboard Shortcuts</a></li>
|
|
- <li class="divider"></li>
|
|
- {% set
|
|
- sections = (
|
|
- (
|
|
- (static_url("a_tour_of_sage/index.html"), "A Tour of Sage", True),
|
|
- (static_url("tutorial/index.html"), "Sage Tutorial", True),
|
|
- (static_url("thematic_tutorials/index.html"), "Thematic Tutorials", True),
|
|
- (static_url("constructions/index.html"), "Sage Constructions", True),
|
|
- (static_url("developer/index.html"), "Developer Guide", True),
|
|
- (static_url("reference/index.html"), "Sage Reference", True),
|
|
- ),
|
|
- (
|
|
- ("http://www.singular.uni-kl.de/Manual/latest/index.htm", "Singular", True),
|
|
- ("http://gap-system.org/Manuals/doc/ref/chap0.html", "GAP", True),
|
|
- ("http://docs.scipy.org/doc/numpy/reference/", "NumPy",True),
|
|
- ("http://docs.scipy.org/doc/scipy/reference/", "SciPy",True),
|
|
- ("http://docs.sympy.org/latest/index.html", "SymPy",True),
|
|
- ),
|
|
- (
|
|
- ("http://ipython.org/documentation.html","IPython Help",True),
|
|
- ("http://nbviewer.ipython.org/github/ipython/ipython/tree/2.x/examples/Index.ipynb", "Notebook Help", True),
|
|
- ("http://docs.python.org","Python",True),
|
|
- ("http://help.github.com/articles/github-flavored-markdown","Markdown",True),
|
|
- ("http://matplotlib.org/contents.html","Matplotlib",True),
|
|
- )
|
|
- )
|
|
- %}
|
|
-
|
|
- {% for helplinks in sections %}
|
|
- {% for link in helplinks %}
|
|
- <li><a href="{{link[0]}}" {{'target="_blank" title="Opens in a new window"' if link[2]}}>
|
|
- {{'<i class="icon-external-link menu-icon pull-right"></i>' if link[2]}}
|
|
- {{link[1]}}
|
|
- </a></li>
|
|
- {% endfor %}
|
|
- {% if not loop.last %}
|
|
- <li class="divider"></li>
|
|
- {% endif %}
|
|
- {% endfor %}
|
|
- </li>
|
|
- </ul>
|
|
- </li>
|
|
- </ul>
|
|
- <div id="kernel_indicator" class="indicator_area pull-right">
|
|
- <i id="kernel_indicator_icon"></i>
|
|
- </div>
|
|
- <div id="modal_indicator" class="indicator_area pull-right">
|
|
- <i id="modal_indicator_icon"></i>
|
|
- </div>
|
|
- <div id="notification_area"></div>
|
|
- </div>
|
|
- </div>
|
|
-</div>
|
|
-</div>
|
|
-<div id="maintoolbar" class="navbar">
|
|
- <div class="toolbar-inner navbar-inner navbar-nobg">
|
|
- <div id="maintoolbar-container" class="container"></div>
|
|
- </div>
|
|
-</div>
|
|
-</div>
|
|
-
|
|
-<div id="ipython-main-app">
|
|
-
|
|
- <div id="notebook_panel">
|
|
- <div id="notebook"></div>
|
|
- <div id="pager_splitter"></div>
|
|
- <div id="pager">
|
|
- <div id='pager_button_area'>
|
|
- </div>
|
|
- <div id="pager-container" class="container"></div>
|
|
- </div>
|
|
- </div>
|
|
-
|
|
-</div>
|
|
-<div id='tooltip' class='ipython_tooltip' style='display:none'></div>
|
|
-
|
|
-
|
|
-{% endblock %}
|
|
-
|
|
-
|
|
-{% block script %}
|
|
-
|
|
-{{super()}}
|
|
-
|
|
-<script src="{{ static_url("components/google-caja/html-css-sanitizer-minified.js") }}" charset="utf-8"></script>
|
|
-<script src="{{ static_url("components/codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
|
|
-<script type="text/javascript">
|
|
- CodeMirror.modeURL = "{{ static_url("components/codemirror/mode/%N/%N.js", include_version=False) }}";
|
|
-</script>
|
|
-<script src="{{ static_url("components/codemirror/addon/mode/loadmode.js") }}" charset="utf-8"></script>
|
|
-<script src="{{ static_url("components/codemirror/addon/mode/multiplex.js") }}" charset="utf-8"></script>
|
|
-<script src="{{ static_url("components/codemirror/addon/mode/overlay.js") }}" charset="utf-8"></script>
|
|
-<script src="{{ static_url("components/codemirror/addon/edit/matchbrackets.js") }}" charset="utf-8"></script>
|
|
-<script src="{{ static_url("components/codemirror/addon/edit/closebrackets.js") }}" charset="utf-8"></script>
|
|
-<script src="{{ static_url("components/codemirror/addon/comment/comment.js") }}" charset="utf-8"></script>
|
|
-<script src="{{ static_url("components/codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script>
|
|
-<script src="{{ static_url("components/codemirror/mode/xml/xml.js") }}" charset="utf-8"></script>
|
|
-<script src="{{ static_url("components/codemirror/mode/javascript/javascript.js") }}" charset="utf-8"></script>
|
|
-<script src="{{ static_url("components/codemirror/mode/css/css.js") }}" charset="utf-8"></script>
|
|
-<script src="{{ static_url("components/codemirror/mode/rst/rst.js") }}" charset="utf-8"></script>
|
|
-<script src="{{ static_url("components/codemirror/mode/markdown/markdown.js") }}" charset="utf-8"></script>
|
|
-<script src="{{ static_url("components/codemirror/mode/gfm/gfm.js") }}" charset="utf-8"></script>
|
|
-<script src="{{ static_url("components/codemirror/mode/python/python.js") }}" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/codemirror-ipython.js") }}" charset="utf-8"></script>
|
|
-
|
|
-<script src="{{ static_url("components/highlight.js/build/highlight.pack.js") }}" charset="utf-8"></script>
|
|
-
|
|
-<script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
|
|
-
|
|
-<script src="{{ static_url("base/js/events.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("base/js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("base/js/keyboard.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("base/js/security.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("base/js/dialog.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("services/kernels/js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("services/kernels/js/comm.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("services/sessions/js/session.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/layoutmanager.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/mathjaxutils.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/celltoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/completer.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/quickhelp.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/pager.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/menubar.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/maintoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/keyboardmanager.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/notificationarea.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/tooltip.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/tour.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-
|
|
-<script src="{{ static_url("notebook/js/config.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/main.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-
|
|
-<script src="{{ static_url("notebook/js/contexthint.js") }}" charset="utf-8"></script>
|
|
-
|
|
-<script src="{{ static_url("notebook/js/celltoolbarpresets/default.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/celltoolbarpresets/rawcell.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{ static_url("notebook/js/celltoolbarpresets/slideshow.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-
|
|
-{% endblock %}
|
|
diff --git a/src/ext/notebook-ipython/templates/page.html b/src/ext/notebook-ipython/templates/page.html
|
|
deleted file mode 100644
|
|
index f971174..0000000
|
|
--- a/src/ext/notebook-ipython/templates/page.html
|
|
+++ /dev/null
|
|
@@ -1,94 +0,0 @@
|
|
-<!DOCTYPE HTML>
|
|
-<html>
|
|
-
|
|
-<head>
|
|
- <meta charset="utf-8">
|
|
-
|
|
- <title>{% block title %}Sage IPython Notebook{% endblock %}</title>
|
|
- <link rel="shortcut icon" type="image/x-icon" href="{{static_url("base/images/favicon.ico") }}">
|
|
- <meta http-equiv="X-UA-Compatible" content="chrome=1">
|
|
- <link rel="stylesheet" href="{{static_url("components/jquery-ui/themes/smoothness/jquery-ui.min.css") }}" type="text/css" />
|
|
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
-
|
|
- {% block stylesheet %}
|
|
- <link rel="stylesheet" href="{{ static_url("style/style.min.css") }}" type="text/css"/>
|
|
- {% endblock %}
|
|
- <link rel="stylesheet" href="{{ static_url("custom/custom.css") }}" type="text/css" />
|
|
- <script src="{{static_url("components/requirejs/require.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
- <script>
|
|
- require.config({
|
|
- baseUrl: '{{static_url("", include_version=False)}}',
|
|
- paths: {
|
|
- nbextensions : '{{ base_url }}nbextensions',
|
|
- underscore : '{{static_url("components/underscore/underscore-min.js")}}',
|
|
- backbone : '{{static_url("components/backbone/backbone-min.js")}}',
|
|
- },
|
|
- shim: {
|
|
- underscore: {
|
|
- exports: '_'
|
|
- },
|
|
- backbone: {
|
|
- deps: ["underscore", "jquery"],
|
|
- exports: "Backbone"
|
|
- }
|
|
- }
|
|
- });
|
|
- </script>
|
|
-
|
|
- {% block meta %}
|
|
- {% endblock %}
|
|
-
|
|
-</head>
|
|
-
|
|
-<body {% block params %}{% endblock %}>
|
|
-
|
|
-<noscript>
|
|
- <div id='noscript'>
|
|
- IPython Notebook requires JavaScript.<br>
|
|
- Please enable it to proceed.
|
|
- </div>
|
|
-</noscript>
|
|
-
|
|
-<div id="header" class="navbar navbar-static-top">
|
|
- <div class="navbar-inner navbar-nobg">
|
|
- <div class="container">
|
|
- <div id="ipython_notebook" class="nav brand pull-left"><a href="{{base_url}}tree/{{notebook_path}}" alt='dashboard'><img src='{{static_url("sage-logo.png") }}' alt='Sage IPython Notebook'/></a></div>
|
|
-
|
|
- {% block login_widget %}
|
|
-
|
|
- <span id="login_widget">
|
|
- {% if logged_in %}
|
|
- <button id="logout">Logout</button>
|
|
- {% elif login_available and not logged_in %}
|
|
- <button id="login">Login</button>
|
|
- {% endif %}
|
|
- </span>
|
|
-
|
|
- {% endblock %}
|
|
-
|
|
- {% block header %}
|
|
- {% endblock %}
|
|
- </div>
|
|
- </div>
|
|
-</div>
|
|
-
|
|
-<div id="site">
|
|
-{% block site %}
|
|
-{% endblock %}
|
|
-</div>
|
|
-
|
|
-<script src="{{static_url("components/jquery/jquery.min.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{static_url("components/jquery-ui/ui/minified/jquery-ui.min.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{static_url("components/bootstrap/bootstrap/js/bootstrap.min.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{static_url("base/js/namespace.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{static_url("base/js/page.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-<script src="{{static_url("auth/js/loginwidget.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-
|
|
-{% block script %}
|
|
-{% endblock %}
|
|
-
|
|
-<script src="{{static_url("custom/custom.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-
|
|
-</body>
|
|
-
|
|
-</html>
|
|
diff --git a/src/ext/notebook-ipython/templates/tree.html b/src/ext/notebook-ipython/templates/tree.html
|
|
deleted file mode 100644
|
|
index 540c828..0000000
|
|
--- a/src/ext/notebook-ipython/templates/tree.html
|
|
+++ /dev/null
|
|
@@ -1,121 +0,0 @@
|
|
-{% extends "page.html" %}
|
|
-
|
|
-{% block title %}{{page_title}}{% endblock %}
|
|
-
|
|
-
|
|
-{% block stylesheet %}
|
|
-{{super()}}
|
|
-<link rel="stylesheet" href="{{ static_url("tree/css/override.css") }}" type="text/css" />
|
|
-{% endblock %}
|
|
-
|
|
-{% block params %}
|
|
-
|
|
-data-project="{{project}}"
|
|
-data-base-url="{{base_url}}"
|
|
-data-notebook-path="{{notebook_path}}"
|
|
-
|
|
-{% endblock %}
|
|
-
|
|
-
|
|
-{% block site %}
|
|
-
|
|
-<div id="ipython-main-app" class="container">
|
|
-
|
|
-<div id="tab_content" class="tabbable">
|
|
- <ul id="tabs" class="nav nav-tabs">
|
|
- <li class="active"><a href="#notebooks" data-toggle="tab">Notebooks</a></li>
|
|
- <li><a href="#running" data-toggle="tab">Running</a></li>
|
|
- <li><a href="#clusters" data-toggle="tab">Clusters</a></li>
|
|
- </ul>
|
|
-
|
|
- <div class="tab-content">
|
|
- <div id="notebooks" class="tab-pane active">
|
|
- <div id="notebook_toolbar" class="row-fluid">
|
|
- <div class="span8">
|
|
- <form id='alternate_upload' class='alternate_upload' >
|
|
- <span id="notebook_list_info" style="position:absolute" >
|
|
- To import a notebook, drag the file onto the listing below or <strong>click here</strong>.
|
|
- </span>
|
|
- <input type="file" name="datafile" class="fileinput" multiple='multiple'>
|
|
- </form>
|
|
- </div>
|
|
- <div class="span4 clearfix">
|
|
- <span id="notebook_buttons" class="pull-right">
|
|
- <button id="new_notebook" title="Create new notebook" class="btn btn-small">New Notebook</button>
|
|
- <button id="refresh_notebook_list" title="Refresh notebook list" class="btn btn-small"><i class="icon-refresh"></i></button>
|
|
- </span>
|
|
- </div>
|
|
- </div>
|
|
-
|
|
- <div id="notebook_list">
|
|
- <div id="notebook_list_header" class="row-fluid list_header">
|
|
- <div id="project_name">
|
|
- <ul class="breadcrumb">
|
|
- <li><a href="{{breadcrumbs[0][0]}}"><i class="icon-home"></i></a><span>/</span></li>
|
|
- {% for crumb in breadcrumbs[1:] %}
|
|
- <li><a href="{{crumb[0]}}">{{crumb[1]}}</a> <span>/</span></li>
|
|
- {% endfor %}
|
|
- </ul>
|
|
- </div>
|
|
- </div>
|
|
- </div>
|
|
- </div>
|
|
-
|
|
- <div id="running" class="tab-pane">
|
|
-
|
|
- <div id="running_toolbar" class="row-fluid">
|
|
- <div class="span8">
|
|
- <span id="running_list_info">Currently running IPython notebooks</span>
|
|
- </div>
|
|
- <div class="span4" class="clearfix">
|
|
- <span id="running_buttons" class="pull-right">
|
|
- <button id="refresh_running_list" title="Refresh running list" class="btn btn-small"><i class="icon-refresh"></i></button>
|
|
- </span>
|
|
- </div>
|
|
- </div>
|
|
-
|
|
- <div id="running_list">
|
|
- <div id="running_list_header" class="row-fluid list_header">
|
|
- <div> There are no notebooks running. </div>
|
|
- </div>
|
|
- </div>
|
|
- </div>
|
|
-
|
|
- <div id="clusters" class="tab-pane">
|
|
-
|
|
- <div id="cluster_toolbar" class="row-fluid">
|
|
- <div class="span8">
|
|
- <span id="cluster_list_info">IPython parallel computing clusters</span>
|
|
- </div>
|
|
- <div class="span4" class="clearfix">
|
|
- <span id="cluster_buttons" class="pull-right">
|
|
- <button id="refresh_cluster_list" title="Refresh cluster list" class="btn btn-small"><i class="icon-refresh"></i></button>
|
|
- </span>
|
|
- </div>
|
|
- </div>
|
|
-
|
|
- <div id="cluster_list">
|
|
- <div id="cluster_list_header" class="row-fluid list_header">
|
|
- <div class="profile_col span4">profile</div>
|
|
- <div class="status_col span3">status</div>
|
|
- <div class="engines_col span3" title="Enter the number of engines to start or empty for default"># of engines</div>
|
|
- <div class="action_col span2">action</div>
|
|
- </div>
|
|
- </div>
|
|
- </div>
|
|
-</div>
|
|
-
|
|
-</div>
|
|
-
|
|
-{% endblock %}
|
|
-
|
|
-{% block script %}
|
|
- {{super()}}
|
|
- <script src="{{ static_url("base/js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
- <script src="{{static_url("base/js/dialog.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
- <script src="{{static_url("tree/js/sessionlist.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
- <script src="{{static_url("tree/js/notebooklist.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
- <script src="{{static_url("tree/js/kernellist.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
- <script src="{{static_url("tree/js/clusterlist.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
- <script src="{{static_url("tree/js/main.js") }}" type="text/javascript" charset="utf-8"></script>
|
|
-{% endblock %}
|
|
diff --git a/src/sage/combinat/root_system/plot.py b/src/sage/combinat/root_system/plot.py
|
|
index 9faff6f..e1d06d4 100644
|
|
--- a/src/sage/combinat/root_system/plot.py
|
|
+++ b/src/sage/combinat/root_system/plot.py
|
|
@@ -731,7 +731,7 @@ class PlotOptions:
|
|
sage: options = RootSystem(["B",3]).root_lattice().plot_parse_options()
|
|
sage: print options.text("coucou", [0,1,2]).x3d_str()
|
|
<Transform translation='0 1 2'>
|
|
- <Shape><Text string='coucou' solid='true'/><Appearance><Material diffuseColor='0.0 0.0 0.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
|
|
+ <Shape><Text string='coucou' solid='true'/><Appearance><Material diffuseColor='0.0 0.0 0.0' shininess='1.0' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
|
|
<BLANKLINE>
|
|
</Transform>
|
|
"""
|
|
diff --git a/src/sage/combinat/tiling.py b/src/sage/combinat/tiling.py
|
|
index c87b07d..6eb2ec5 100644
|
|
--- a/src/sage/combinat/tiling.py
|
|
+++ b/src/sage/combinat/tiling.py
|
|
@@ -177,17 +177,16 @@ Animation of the solutions::
|
|
sage: from sage.combinat.tiling import Polyomino, TilingSolver
|
|
sage: Y = Polyomino([(0,0),(1,0),(2,0),(3,0),(2,1)], color='yellow')
|
|
sage: T = TilingSolver([Y], box=(15,15), reusable=True, reflection=True)
|
|
- sage: a = T.animate(stop=40) # long time
|
|
- sage: a # long time
|
|
+ sage: a = T.animate(stop=40) # long time # optional -- ImageMagick
|
|
+ sage: a # long time # optional -- ImageMagick
|
|
Animation with 40 frames
|
|
- sage: a.show() # not tested - requires convert command
|
|
|
|
Incremental animation of the solutions (one piece is removed/added at a time)::
|
|
|
|
- sage: a = T.animate('incremental', stop=40) # long time
|
|
- sage: a # long time
|
|
+ sage: a = T.animate('incremental', stop=40) # long time # optional -- ImageMagick
|
|
+ sage: a # long time # optional -- ImageMagick
|
|
Animation with 40 frames
|
|
- sage: a.show(delay=50, iterations=1) # not tested - requires convert command
|
|
+ sage: a.show(delay=50, iterations=1) # long time # optional -- ImageMagick
|
|
|
|
5d Easy Example
|
|
---------------
|
|
@@ -1650,20 +1649,20 @@ class TilingSolver(SageObject):
|
|
sage: y = Polyomino([(0,0),(1,0),(2,0),(3,0),(2,1)], color='cyan')
|
|
sage: T = TilingSolver([y], box=(5,10), reusable=True, reflection=True)
|
|
sage: a = T.animate()
|
|
- sage: a
|
|
+ sage: a # optional -- ImageMagick
|
|
Animation with 10 frames
|
|
|
|
Include partial solutions (common prefix between two consecutive
|
|
solutions)::
|
|
|
|
sage: a = T.animate('common_prefix')
|
|
- sage: a
|
|
+ sage: a # optional -- ImageMagick
|
|
Animation with 19 frames
|
|
|
|
Incremental solutions (one piece removed or added at a time)::
|
|
|
|
sage: a = T.animate('incremental') # long time (2s)
|
|
- sage: a # long time (2s)
|
|
+ sage: a # long time (2s) # optional -- ImageMagick
|
|
Animation with 123 frames
|
|
|
|
::
|
|
diff --git a/src/sage/combinat/words/paths.py b/src/sage/combinat/words/paths.py
|
|
index d1dfa32..c43426e 100644
|
|
--- a/src/sage/combinat/words/paths.py
|
|
+++ b/src/sage/combinat/words/paths.py
|
|
@@ -1625,16 +1625,16 @@ class FiniteWordPath_2d(FiniteWordPath_all):
|
|
|
|
sage: P = WordPaths('abAB')
|
|
sage: p = P('aaababbb')
|
|
- sage: a = p.animate(); a
|
|
+ sage: a = p.animate(); a # optional -- ImageMagick
|
|
Animation with 9 frames
|
|
- sage: show(a) # optional -- ImageMagick
|
|
- sage: a.gif(delay=35, iterations=3) # optional
|
|
+ sage: show(a) # optional -- ImageMagick
|
|
+ sage: a.gif(delay=35, iterations=3) # optional -- ImageMagick
|
|
|
|
::
|
|
|
|
sage: P = WordPaths('abcdef',steps='triangle')
|
|
sage: p = P('abcdef')
|
|
- sage: p.animate()
|
|
+ sage: p.animate() # optional -- ImageMagick
|
|
Animation with 8 frames
|
|
|
|
If the path is closed, the plain polygon is added at the end of the
|
|
@@ -1642,7 +1642,7 @@ class FiniteWordPath_2d(FiniteWordPath_all):
|
|
|
|
sage: P = WordPaths('abAB')
|
|
sage: p = P('ababAbABABaB')
|
|
- sage: a = p.animate(); a
|
|
+ sage: a = p.animate(); a # optional -- ImageMagick
|
|
Animation with 14 frames
|
|
|
|
Another example illustrating a Fibonacci tile::
|
|
diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py
|
|
index 5e8d37f..fde65c3 100644
|
|
--- a/src/sage/doctest/forker.py
|
|
+++ b/src/sage/doctest/forker.py
|
|
@@ -101,8 +101,12 @@ def init_sage():
|
|
sage.doctest.DOCTEST_MODE=True
|
|
import sage.all_cmdline
|
|
sage.interfaces.quit.invalidate_all()
|
|
- import sage.repl.display.python_hook
|
|
- sys.displayhook = sage.repl.display.python_hook.DoctestDisplayHook()
|
|
+
|
|
+ # Use the rich output backend for doctest
|
|
+ from sage.repl.rich_output import get_display_manager
|
|
+ dm = get_display_manager()
|
|
+ from sage.repl.rich_output.backend_doctest import BackendDoctest
|
|
+ dm.switch_backend(BackendDoctest())
|
|
|
|
# Switch on extra debugging
|
|
from sage.structure.debug_options import debug
|
|
diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py
|
|
index c1e8e6c..b6b78e2 100644
|
|
--- a/src/sage/geometry/polyhedron/base.py
|
|
+++ b/src/sage/geometry/polyhedron/base.py
|
|
@@ -679,7 +679,33 @@ class Polyhedron_base(Element):
|
|
.format(self.ambient_dim()))
|
|
return plot_method(*opts)
|
|
|
|
- show = plot
|
|
+ def show(self, **kwds):
|
|
+ """
|
|
+ Display graphics immediately
|
|
+
|
|
+ This method attempts to display the graphics immediately,
|
|
+ without waiting for the currently running code (if any) to
|
|
+ return to the command line. Be careful, calling it from within
|
|
+ a loop will potentially launch a large number of external
|
|
+ viewer programs.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``kwds`` -- optional keyword arguments. See :meth:`plot` for
|
|
+ the description of available options.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ This method does not return anything. Use :meth:`plot` if you
|
|
+ want to generate a graphics object that can be saved or
|
|
+ further transformed.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: square = polytopes.n_cube(2)
|
|
+ sage: square.show(point='red')
|
|
+ """
|
|
+ self.plot(**kwds).show()
|
|
|
|
def _repr_(self):
|
|
"""
|
|
diff --git a/src/sage/geometry/polyhedron/library.py b/src/sage/geometry/polyhedron/library.py
|
|
index ca8c03c..14ae9bb 100644
|
|
--- a/src/sage/geometry/polyhedron/library.py
|
|
+++ b/src/sage/geometry/polyhedron/library.py
|
|
@@ -595,7 +595,7 @@ class Polytopes():
|
|
sage: perm4 = polytopes.permutahedron(4)
|
|
sage: perm4
|
|
A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 24 vertices
|
|
- sage: polytopes.permutahedron(5).show() # long time
|
|
+ sage: polytopes.permutahedron(5).plot() # long time
|
|
Graphics3d Object
|
|
"""
|
|
verts = range(1,n+1)
|
|
diff --git a/src/sage/geometry/polyhedron/plot.py b/src/sage/geometry/polyhedron/plot.py
|
|
index d88fcd7..ee838d9 100644
|
|
--- a/src/sage/geometry/polyhedron/plot.py
|
|
+++ b/src/sage/geometry/polyhedron/plot.py
|
|
@@ -726,20 +726,6 @@ class Projection(SageObject):
|
|
deprecation(16625, 'use Projection.plot instead')
|
|
return self.plot(*args, **kwds)
|
|
|
|
- def _graphics_(self, **kwds):
|
|
- """
|
|
- Display projection graphically on the Sage command line.
|
|
-
|
|
- See :meth:`~sage.plot.graphics.Graphics._graphics_`.
|
|
-
|
|
- EXAMPLES::
|
|
-
|
|
- sage: polytopes.n_cube(3).projection()._graphics_(
|
|
- ....: mime_types={'image/png'})
|
|
- Graphics file image/png
|
|
- """
|
|
- return self.plot()._graphics_(**kwds)
|
|
-
|
|
def _init_from_2d(self, polyhedron):
|
|
"""
|
|
Internal function: Initialize from polyhedron in
|
|
@@ -762,7 +748,6 @@ class Projection(SageObject):
|
|
self._init_lines_arrows(polyhedron)
|
|
self._init_area_2d(polyhedron)
|
|
|
|
-
|
|
def _init_from_3d(self, polyhedron):
|
|
"""
|
|
Internal function: Initialize from polyhedron in
|
|
diff --git a/src/sage/interfaces/mathematica.py b/src/sage/interfaces/mathematica.py
|
|
index 8121167..e56675e 100644
|
|
--- a/src/sage/interfaces/mathematica.py
|
|
+++ b/src/sage/interfaces/mathematica.py
|
|
@@ -365,6 +365,7 @@ AUTHORS:
|
|
import os
|
|
import re
|
|
|
|
+from sage.misc.cachefunc import cached_method
|
|
from expect import (Expect, ExpectElement, ExpectFunction,
|
|
FunctionElement, AsciiArtString)
|
|
|
|
@@ -819,9 +820,94 @@ class MathematicaElement(ExpectElement):
|
|
"""
|
|
return self.Length()
|
|
|
|
- def show(self, filename=None, ImageSize=600):
|
|
+ @cached_method
|
|
+ def _is_graphics(self):
|
|
+ """
|
|
+ Test whether the mathematica expression is graphics
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Boolean.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: P = mathematica('Plot[Sin[x],{x,-2Pi,4Pi}]') # optional - mathematica
|
|
+ sage: P._is_graphics() # optional - mathematica
|
|
+ True
|
|
+ """
|
|
+ P = self._check_valid()
|
|
+ return P.eval('InputForm[%s]' % self.name()).strip().startswith('Graphics[')
|
|
+
|
|
+ def save_image(self, filename, ImageSize=600):
|
|
r"""
|
|
- Show a mathematica expression or plot in the Sage notebook.
|
|
+ Save a mathematica graphics
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``filename`` -- string. The filename to save as. The
|
|
+ extension determines the image file format.
|
|
+
|
|
+ - ``ImageSize`` -- integer. The size of the resulting image.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: P = mathematica('Plot[Sin[x],{x,-2Pi,4Pi}]') # optional - mathematica
|
|
+ sage: filename = tmp_filename() # optional - mathematica
|
|
+ sage: P.save(filename, ImageSize=800) # optional - mathematica
|
|
+ """
|
|
+ P = self._check_valid()
|
|
+ if not self._is_graphics():
|
|
+ raise ValueError('mathematica expression is not graphics')
|
|
+ filename = os.path.abspath(filename)
|
|
+ s = 'Export["%s", %s, ImageSize->%s]'%(filename, self.name(), ImageSize)
|
|
+ P.eval(s)
|
|
+
|
|
+ def _rich_repr_(self, display_manager, **kwds):
|
|
+ """
|
|
+ Rich Output Magic Method
|
|
+
|
|
+ See :mod:`sage.repl.rich_output` for details.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: P = mathematica('Plot[Sin[x],{x,-2Pi,4Pi}]') # optional - mathematica
|
|
+ sage: P._rich_repr_(dm) # optional - mathematica
|
|
+ OutputImagePng container
|
|
+ """
|
|
+ if self._is_graphics():
|
|
+ OutputImagePng = display_manager.types.OutputImagePng
|
|
+ if display_manager.preferences.graphics == 'disable':
|
|
+ return
|
|
+ if OutputImagePng in display_manager.supported_output():
|
|
+ return display_manager.graphics_from_save(
|
|
+ self.save, kwds, '.png', OutputImagePng)
|
|
+ else:
|
|
+ OutputLatex = display_manager.types.OutputLatex
|
|
+ if display_manager.preferences.text == 'plain':
|
|
+ return
|
|
+ if OutputLatex in display_manager.supported_output():
|
|
+ return OutputLatex(self._latex_())
|
|
+
|
|
+ def show(self, ImageSize=600):
|
|
+ r"""
|
|
+ Show a mathematica expression immediately.
|
|
+
|
|
+ This method attempts to display the graphics immediately,
|
|
+ without waiting for the currently running code (if any) to
|
|
+ return to the command line. Be careful, calling it from within
|
|
+ a loop will potentially launch a large number of external
|
|
+ viewer programs.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``ImageSize`` -- integer. The size of the resulting image.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ This method does not return anything. Use :meth:`save` if you
|
|
+ want to save the figure as an image.
|
|
|
|
EXAMPLES::
|
|
|
|
@@ -832,19 +918,10 @@ class MathematicaElement(ExpectElement):
|
|
sage: show(Q) # optional - mathematica
|
|
<html><div class="math">\frac{\sin (x \cos (y))}{\sqrt{1-x^2}}</div></html>
|
|
"""
|
|
- P = self._check_valid()
|
|
- if P.eval('InputForm[%s]' % self.name()).strip().startswith('Graphics['):
|
|
- if filename is None:
|
|
- from sage.misc.temporary_file import graphics_filename
|
|
- filename = graphics_filename()
|
|
- orig_dir = P.eval('Directory[]').strip()
|
|
- P.chdir(os.path.abspath("."))
|
|
- s = 'Export["%s", %s, ImageSize->%s]'%(filename, self.name(), ImageSize)
|
|
- P.eval(s)
|
|
- P.chdir(orig_dir)
|
|
- else:
|
|
- print '<html><div class="math">%s</div></html>' % self._latex_()
|
|
-
|
|
+ from sage.repl.rich_output import get_display_manager
|
|
+ dm = get_display_manager()
|
|
+ dm.display_immediately(self, ImageSize=ImageSize)
|
|
+
|
|
def str(self):
|
|
return str(self)
|
|
|
|
diff --git a/src/sage/interfaces/sage0.py b/src/sage/interfaces/sage0.py
|
|
index 0e1a50b..8401dc8 100644
|
|
--- a/src/sage/interfaces/sage0.py
|
|
+++ b/src/sage/interfaces/sage0.py
|
|
@@ -426,17 +426,18 @@ class Sage(Expect):
|
|
|
|
class SageElement(ExpectElement):
|
|
|
|
- def _graphics_(self, **kwds):
|
|
+ def _rich_repr_(self, display_manager, **kwds):
|
|
"""
|
|
- Disable graphical output.
|
|
+ Disable rich output
|
|
|
|
This is necessary because otherwise our :meth:`__getattr__`
|
|
would be called.
|
|
|
|
EXAMPLES::
|
|
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
sage: m = sage0(4)
|
|
- sage: m._graphics_() is None
|
|
+ sage: m._rich_repr_(get_display_manager()) is None
|
|
True
|
|
"""
|
|
return None
|
|
diff --git a/src/sage/interfaces/tachyon.py b/src/sage/interfaces/tachyon.py
|
|
index 8930468..b7da4ae 100644
|
|
--- a/src/sage/interfaces/tachyon.py
|
|
+++ b/src/sage/interfaces/tachyon.py
|
|
@@ -15,13 +15,16 @@ AUTHOR:
|
|
# http://www.gnu.org/licenses/
|
|
#*****************************************************************************
|
|
|
|
+import os
|
|
+import sys
|
|
+
|
|
from sage.misc.pager import pager
|
|
from sage.misc.temporary_file import tmp_filename
|
|
from sage.misc.sagedoc import format
|
|
-import os
|
|
-import sys
|
|
+from sage.structure.sage_object import SageObject
|
|
+
|
|
|
|
-class TachyonRT:
|
|
+class TachyonRT(SageObject):
|
|
"""
|
|
The Tachyon Ray Tracer
|
|
|
|
@@ -64,7 +67,6 @@ class TachyonRT:
|
|
|
|
OUTPUT:
|
|
|
|
-
|
|
- Some text may be displayed onscreen.
|
|
|
|
- The file outfile is created.
|
|
@@ -72,11 +74,10 @@ class TachyonRT:
|
|
|
|
EXAMPLES:
|
|
|
|
- AUTHORS:
|
|
-
|
|
- - John E. Stone
|
|
+
|
|
+ .. automethod:: __call__
|
|
"""
|
|
- def __repr__(self):
|
|
+ def _repr_(self):
|
|
"""
|
|
Returns a brief description of this interface object (the Tachyon raytracer written by John Stone).
|
|
|
|
@@ -89,13 +90,23 @@ class TachyonRT:
|
|
"""
|
|
return "John Stone's Tachyon Ray Tracer"
|
|
|
|
- def __call__(self, model, outfile='sage.png',
|
|
- verbose=1, block=True, extra_opts=''):
|
|
+ def __call__(self, model, outfile='sage.png', verbose=1, extra_opts=''):
|
|
"""
|
|
This executes the tachyon program, given a scene file input.
|
|
- The default is to return the result as a PNG file called 'sage.png'.
|
|
|
|
- TESTS::
|
|
+ INPUT:
|
|
+
|
|
+ - ``model`` -- string. The tachyon model.
|
|
+
|
|
+ - ``outfile`` -- string, default ``'sage.png'``. The filename
|
|
+ to save the model to.
|
|
+
|
|
+ - ``verbose`` -- 0, 1, (default) or 2. The verbosity level.
|
|
+
|
|
+ - ``extra_opts`` -- string (default: empty string). Extra
|
|
+ options that will be appended to the tachyon commandline.
|
|
+
|
|
+ EXAMPLES::
|
|
|
|
sage: from sage.interfaces.tachyon import TachyonRT
|
|
sage: tgen = Tachyon()
|
|
@@ -105,45 +116,51 @@ class TachyonRT:
|
|
'resolution'
|
|
sage: t = TachyonRT()
|
|
sage: import os
|
|
- sage: t(tgen.str(), outfile = os.devnull)
|
|
+ sage: t(tgen.str(), outfile=os.devnull)
|
|
tachyon ...
|
|
Tachyon Parallel/Multiprocessor Ray Tracer...
|
|
+
|
|
+ TESTS::
|
|
+
|
|
+ sage: from sage.env import SAGE_EXTCODE
|
|
+ sage: filename = os.path.join(SAGE_EXTCODE, 'doctest', 'invalid', 'syntax_error.tachyon')
|
|
+ sage: syntax_error = open(filename, 'r').read()
|
|
+ sage: t(syntax_error, outfile=os.devnull)
|
|
+ Traceback (most recent call last):
|
|
+ ...
|
|
+ RuntimeError: Tachyon Parallel/Multiprocessor Ray Tracer Version 0.98.9
|
|
+ ...
|
|
+ Parser failed due to an input file syntax error.
|
|
+ Aborting render.
|
|
"""
|
|
modelfile = tmp_filename(ext='.dat')
|
|
open(modelfile,'w').write(model)
|
|
- opts = ''
|
|
+ cmd = ['tachyon', modelfile]
|
|
ext = outfile[-4:].lower()
|
|
if ext == '.png':
|
|
- opts += ' -format PNG '
|
|
+ cmd += ['-format', 'PNG']
|
|
elif ext == '.tga':
|
|
- opts += ' -format TARGA '
|
|
+ cmd += ['-format', 'TARGA']
|
|
elif ext == '.bmp':
|
|
- opts += ' -format BMP '
|
|
+ cmd += ['-format', 'BMP']
|
|
elif ext == '.ppm':
|
|
- opts += ' -format PPM '
|
|
+ cmd += ['-format', 'PPM']
|
|
elif ext == '.rgb':
|
|
- opts += ' -format RGB '
|
|
-
|
|
- opts += ' -o %s '%outfile
|
|
-
|
|
- opts += ' ' + extra_opts + ' '
|
|
-
|
|
+ cmd += ['-format', 'RGB']
|
|
+ cmd += ['-o', outfile]
|
|
+ cmd += extra_opts.split()
|
|
if verbose >= 2:
|
|
- opts += ' +V '
|
|
- elif verbose == 0:
|
|
- opts += ' 1>/dev/null'
|
|
-
|
|
- cmd = 'tachyon %s %s; rm -f "%s"'%(modelfile,opts, modelfile)
|
|
-
|
|
- if not block:
|
|
- cmd = '( ' + cmd + ' ) &'
|
|
-
|
|
+ cmd += ['+V']
|
|
if verbose:
|
|
- print cmd
|
|
- # One should always flush before system()
|
|
- sys.stdout.flush()
|
|
- sys.stderr.flush()
|
|
- os.system(cmd)
|
|
+ print(' '.join(cmd))
|
|
+ import subprocess
|
|
+ out = subprocess.check_output(cmd)
|
|
+ if verbose >= 1:
|
|
+ print(out)
|
|
+ if out.rstrip().endswith('Aborting render.'):
|
|
+ raise RuntimeError(out)
|
|
+ if outfile != os.devnull and os.stat(outfile).st_size == 0:
|
|
+ raise RuntimeError('tachyon did not abort but output file is empty')
|
|
|
|
def usage(self, use_pager=True):
|
|
"""
|
|
diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx
|
|
index 45454ee..27daca9 100644
|
|
--- a/src/sage/matrix/matrix2.pyx
|
|
+++ b/src/sage/matrix/matrix2.pyx
|
|
@@ -8310,11 +8310,9 @@ cdef class Matrix(matrix1.Matrix):
|
|
from sage.combinat.permutation import bistochastic_as_sum_of_permutations
|
|
return bistochastic_as_sum_of_permutations(self)
|
|
|
|
-
|
|
- def visualize_structure(self, filename=None, maxsize=512):
|
|
- """
|
|
- Write a PNG image to 'filename' which visualizes self by putting
|
|
- black pixels in those positions which have nonzero entries.
|
|
+ def visualize_structure(self, maxsize=512):
|
|
+ r"""
|
|
+ Visualize the non-zero entries
|
|
|
|
White pixels are put at positions with zero entries. If 'maxsize'
|
|
is given, then the maximal dimension in either x or y direction is
|
|
@@ -8326,62 +8324,56 @@ cdef class Matrix(matrix1.Matrix):
|
|
|
|
INPUT:
|
|
|
|
+ - ``maxsize`` - integer (default: ``512``). Maximal dimension
|
|
+ in either x or y direction of the resulting image. If
|
|
+ ``None`` or a maxsize larger than
|
|
+ ``max(self.nrows(),self.ncols())`` is given the image will
|
|
+ have the same pixelsize as the matrix dimensions.
|
|
|
|
- - ``filename`` - either a path or None in which case a
|
|
- filename in the current directory is chosen automatically
|
|
- (default:None)
|
|
-
|
|
+ OUTPUT:
|
|
|
|
- maxsize - maximal dimension in either x or y direction of the
|
|
- resulting image. If None or a maxsize larger than
|
|
- max(self.nrows(),self.ncols()) is given the image will have the
|
|
- same pixelsize as the matrix dimensions (default: 512)
|
|
+ Bitmap image as an instance of
|
|
+ :class:`~sage.repl.image.Image`.
|
|
|
|
EXAMPLE::
|
|
|
|
- sage: M = random_matrix(CC, 4)
|
|
- sage: M.visualize_structure()
|
|
- """
|
|
- import gd
|
|
- import os
|
|
+ sage: M = random_matrix(CC, 5, 7)
|
|
+ sage: for i in range(5): M[i,i] = 0
|
|
+ sage: M[4, 0] = M[0, 6] = M[4, 6] = 0
|
|
+ sage: img = M.visualize_structure(); img
|
|
+ 7x5px 24-bit RGB image
|
|
|
|
+ You can use :meth:`~sage.repl.image.Image.save` to save the
|
|
+ resulting image::
|
|
+
|
|
+ sage: filename = tmp_filename(ext='.png')
|
|
+ sage: img.save(filename)
|
|
+ sage: open(filename).read().startswith('\x89PNG')
|
|
+ True
|
|
+ """
|
|
cdef int x, y, _x, _y, v, bi, bisq
|
|
cdef int ir,ic
|
|
cdef float b, fct
|
|
-
|
|
mr, mc = self.nrows(), self.ncols()
|
|
-
|
|
if maxsize is None:
|
|
-
|
|
ir = mc
|
|
ic = mr
|
|
b = 1.0
|
|
-
|
|
elif max(mr,mc) > maxsize:
|
|
-
|
|
maxsize = float(maxsize)
|
|
ir = int(mc * maxsize/max(mr,mc))
|
|
ic = int(mr * maxsize/max(mr,mc))
|
|
b = max(mr,mc)/maxsize
|
|
-
|
|
else:
|
|
-
|
|
ir = mc
|
|
ic = mr
|
|
b = 1.0
|
|
-
|
|
bi = round(b)
|
|
bisq = bi*bi
|
|
fct = 255.0/bisq
|
|
-
|
|
- im = gd.image((ir,ic),1)
|
|
- white = im.colorExact((255,255,255))
|
|
- im.fill((0,0),white)
|
|
-
|
|
- # these speed things up a bit
|
|
- colorExact = im.colorExact
|
|
- setPixel = im.setPixel
|
|
-
|
|
+ from sage.repl.image import Image
|
|
+ img = Image('RGB', (ir, ic))
|
|
+ pixel = img.pixels()
|
|
for x from 0 <= x < ic:
|
|
for y from 0 <= y < ir:
|
|
v = bisq
|
|
@@ -8389,16 +8381,9 @@ cdef class Matrix(matrix1.Matrix):
|
|
for _y from 0 <= _y < bi:
|
|
if not self.get_unsafe(<int>(x*b + _x), <int>(y*b + _y)).is_zero():
|
|
v-=1 #increase darkness
|
|
-
|
|
- v = round(v*fct)
|
|
- val = colorExact((v,v,v))
|
|
- setPixel((y,x), val)
|
|
-
|
|
- if filename is None:
|
|
- from sage.misc.temporary_file import graphics_filename
|
|
- filename = graphics_filename()
|
|
-
|
|
- im.writePng(filename)
|
|
+ v = round(v*fct)
|
|
+ pixel[y, x] = (v, v, v)
|
|
+ return img
|
|
|
|
def density(self):
|
|
"""
|
|
diff --git a/src/sage/matrix/matrix_modn_sparse.pyx b/src/sage/matrix/matrix_modn_sparse.pyx
|
|
index f30b01b..be1cee5 100644
|
|
--- a/src/sage/matrix/matrix_modn_sparse.pyx
|
|
+++ b/src/sage/matrix/matrix_modn_sparse.pyx
|
|
@@ -563,7 +563,7 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse):
|
|
return nzp
|
|
|
|
def visualize_structure(self, filename=None, maxsize=512):
|
|
- """
|
|
+ r"""
|
|
Write a PNG image to 'filename' which visualizes self by putting
|
|
black pixels in those positions which have nonzero entries.
|
|
|
|
@@ -577,78 +577,63 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse):
|
|
|
|
INPUT:
|
|
|
|
+ - ``filename`` -- String. Name of the filename to save the
|
|
+ resulting image.
|
|
|
|
- - ``filename`` - either a path or None in which case a
|
|
- filename in the current directory is chosen automatically
|
|
- (default:None)
|
|
-
|
|
- - ``maxsize`` - maximal dimension in either x or y
|
|
- direction of the resulting image. If None or a maxsize larger than
|
|
- max(self.nrows(),self.ncols()) is given the image will have the
|
|
- same pixelsize as the matrix dimensions (default: 512)
|
|
+ - ``maxsize`` - integer (default: ``512``). Maximal dimension
|
|
+ in either x or y direction of the resulting image. If
|
|
+ ``None`` or a maxsize larger than
|
|
+ ``max(self.nrows(),self.ncols())`` is given the image will
|
|
+ have the same pixelsize as the matrix dimensions.
|
|
|
|
EXAMPLES::
|
|
|
|
sage: M = Matrix(GF(7), [[0,0,0,1,0,0,0,0],[0,1,0,0,0,0,1,0]], sparse=True); M
|
|
[0 0 0 1 0 0 0 0]
|
|
[0 1 0 0 0 0 1 0]
|
|
- sage: M.visualize_structure()
|
|
- """
|
|
- import gd
|
|
- import os
|
|
+ sage: img = M.visualize_structure(); img
|
|
+ 8x2px 24-bit RGB image
|
|
|
|
+ You can use :meth:`~sage.repl.image.Image.save` to save the
|
|
+ resulting image::
|
|
+
|
|
+ sage: filename = tmp_filename(ext='.png')
|
|
+ sage: img.save(filename)
|
|
+ sage: open(filename).read().startswith('\x89PNG')
|
|
+ True
|
|
+ """
|
|
cdef Py_ssize_t i, j, k
|
|
cdef float blk,invblk
|
|
cdef int delta
|
|
cdef int x,y,r,g,b
|
|
-
|
|
mr, mc = self.nrows(), self.ncols()
|
|
-
|
|
if maxsize is None:
|
|
-
|
|
ir = mc
|
|
ic = mr
|
|
blk = 1.0
|
|
invblk = 1.0
|
|
-
|
|
elif max(mr,mc) > maxsize:
|
|
-
|
|
maxsize = float(maxsize)
|
|
ir = int(mc * maxsize/max(mr,mc))
|
|
ic = int(mr * maxsize/max(mr,mc))
|
|
blk = max(mr,mc)/maxsize
|
|
invblk = maxsize/max(mr,mc)
|
|
-
|
|
else:
|
|
-
|
|
ir = mc
|
|
ic = mr
|
|
blk = 1.0
|
|
invblk = 1.0
|
|
-
|
|
delta = <int>(255.0 / blk*blk)
|
|
-
|
|
- im = gd.image((ir,ic),1)
|
|
- white = im.colorExact((255,255,255))
|
|
- im.fill((0,0),white)
|
|
-
|
|
- colorComponents = im.colorComponents
|
|
- getPixel = im.getPixel
|
|
- setPixel = im.setPixel
|
|
- colorExact = im.colorExact
|
|
-
|
|
+ from sage.repl.image import Image
|
|
+ img = Image('RGB', (ir, ic), (255, 255, 255))
|
|
+ pixel = img.pixels()
|
|
for i from 0 <= i < self._nrows:
|
|
for j from 0 <= j < self.rows[i].num_nonzero:
|
|
x = <int>(invblk * self.rows[i].positions[j])
|
|
y = <int>(invblk * i)
|
|
- r,g,b = colorComponents( getPixel((x,y)))
|
|
- setPixel( (x,y), colorExact((r-delta,g-delta,b-delta)) )
|
|
-
|
|
- if filename is None:
|
|
- from sage.misc.temporary_file import graphics_filename
|
|
- filename = graphics_filename()
|
|
-
|
|
- im.writePng(filename)
|
|
+ r, g, b = pixel[x, y]
|
|
+ pixel[x, y] = (r-delta, g-delta, b-delta)
|
|
+ return img
|
|
|
|
def density(self):
|
|
"""
|
|
diff --git a/src/sage/misc/ascii_art.py b/src/sage/misc/ascii_art.py
|
|
index 39a2298..2c5efd0 100644
|
|
--- a/src/sage/misc/ascii_art.py
|
|
+++ b/src/sage/misc/ascii_art.py
|
|
@@ -65,6 +65,7 @@ manager activated by the magic function: ``%display ascii_art``::
|
|
]
|
|
***** ]
|
|
sage: shell.run_cell('%display simple')
|
|
+ sage: shell.quit()
|
|
|
|
::
|
|
|
|
@@ -150,9 +151,12 @@ manager activated by the magic function: ``%display ascii_art``::
|
|
#
|
|
# http://www.gnu.org/licenses/
|
|
#*******************************************************************************
|
|
+
|
|
+import os, sys
|
|
from sage.structure.sage_object import SageObject
|
|
from sage.rings.integer import Integer
|
|
|
|
+
|
|
################################################################################
|
|
### Global variable use to compute the maximal length allows for ascii art
|
|
### object.
|
|
@@ -244,8 +248,6 @@ class AsciiArt(SageObject):
|
|
sage: repr(p5)
|
|
' * \n * * \n*****'
|
|
"""
|
|
- #return "\n".join(self._matrix)
|
|
- import os
|
|
# Compute the max length of a draw
|
|
global MAX_WIDTH
|
|
if MAX_WIDTH is not None:
|
|
@@ -352,6 +354,34 @@ class AsciiArt(SageObject):
|
|
"""
|
|
return self._breakpoints
|
|
|
|
+ def _isatty(self):
|
|
+ """
|
|
+ Test whether stdout is a TTY
|
|
+
|
|
+ If this test succeeds, you can assume that stdout is directly
|
|
+ connected to a terminal. Otherwise you should treat stdout as
|
|
+ being redirected to a file.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Boolean
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.misc.ascii_art import empty_ascii_art
|
|
+ sage: empty_ascii_art._isatty()
|
|
+ False
|
|
+ """
|
|
+ from sage.doctest import DOCTEST_MODE
|
|
+ if DOCTEST_MODE:
|
|
+ return False
|
|
+ try:
|
|
+ return os.isatty(sys.stdout.fileno())
|
|
+ except Exception:
|
|
+ # The IPython zeromq kernel uses a fake stdout that does
|
|
+ # not support fileno()
|
|
+ return False
|
|
+
|
|
def _terminal_width(self):
|
|
"""
|
|
Compute the width size of the terminal.
|
|
@@ -362,10 +392,7 @@ class AsciiArt(SageObject):
|
|
sage: empty_ascii_art._terminal_width()
|
|
80
|
|
"""
|
|
- import os, sys
|
|
- from sage.doctest.__init__ import DOCTEST_MODE
|
|
- isatty = os.isatty(sys.stdout.fileno())
|
|
- if DOCTEST_MODE or not isatty:
|
|
+ if not self._isatty():
|
|
return 80
|
|
import fcntl, termios, struct
|
|
rc = fcntl.ioctl(int(0), termios.TIOCGWINSZ,
|
|
diff --git a/src/sage/misc/displayhook.py b/src/sage/misc/displayhook.py
|
|
index 9e86dfe..88edfe4 100644
|
|
--- a/src/sage/misc/displayhook.py
|
|
+++ b/src/sage/misc/displayhook.py
|
|
@@ -1,28 +1,49 @@
|
|
+# -*- encoding: utf-8 -*-
|
|
"""
|
|
-Old Displayhook
|
|
+Entrypoint for the SageNB Display System
|
|
|
|
Just for compatibility with the notebook, you should not use this any
|
|
-more. Look into ``sage/repl/display`` instead.
|
|
+more. Look into ``sage/repl/`` instead.
|
|
"""
|
|
|
|
-from sage.repl.display.python_hook import DoctestDisplayHook
|
|
-from sage.repl.display.formatter import SageNBTextFormatter
|
|
+#*****************************************************************************
|
|
+# Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com>
|
|
+#
|
|
+# Distributed under the terms of the GNU General Public License (GPL)
|
|
+# as published by the Free Software Foundation; either version 2 of
|
|
+# the License, or (at your option) any later version.
|
|
+# http://www.gnu.org/licenses/
|
|
+#*****************************************************************************
|
|
|
|
+from sage.repl.rich_output import get_display_manager
|
|
+from sage.repl.rich_output.backend_sagenb import BackendSageNB
|
|
|
|
-class DisplayHook(DoctestDisplayHook):
|
|
|
|
- def __init__(self):
|
|
- """
|
|
- Python constructor
|
|
+def DisplayHook():
|
|
+ """
|
|
+ This function is called by SageNB to set up its displayhook.
|
|
|
|
- EXAMPLES::
|
|
+ OUTPUT:
|
|
+
|
|
+ The new displayhook that will be used by SageNB
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.misc.displayhook import DisplayHook
|
|
+ sage: d = DisplayHook()
|
|
+ sage: d
|
|
+ <bound method DisplayManager.displayhook of The
|
|
+ Sage display manager using the SageNB backend>
|
|
+
|
|
+ sage: d(set([1, 2, 3])) # Sage commandline output
|
|
+ {1, 2, 3}
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: get_display_manager()
|
|
+ The Sage display manager using the SageNB backend
|
|
+ """
|
|
+ display_manager = get_display_manager()
|
|
+ backend = BackendSageNB()
|
|
+ display_manager.switch_backend(backend)
|
|
+ return display_manager.displayhook
|
|
|
|
- sage: from sage.repl.display.python_hook import DoctestDisplayHook
|
|
- sage: d = DoctestDisplayHook()
|
|
- sage: d(set([1, 2, 3])) # Sage commandline output
|
|
- {1, 2, 3}
|
|
- sage: print(set([1, 2, 3])) # Plain Python output
|
|
- set([1, 2, 3])
|
|
- """
|
|
- # do not call super, we set our own formatter
|
|
- self.formatter = SageNBTextFormatter()
|
|
diff --git a/src/sage/misc/latex.py b/src/sage/misc/latex.py
|
|
index 8d95c0f..00e85c9 100644
|
|
--- a/src/sage/misc/latex.py
|
|
+++ b/src/sage/misc/latex.py
|
|
@@ -59,7 +59,7 @@ import random
|
|
import subprocess
|
|
import types
|
|
|
|
-from sage.misc.temporary_file import tmp_dir, graphics_filename
|
|
+from sage.misc.temporary_file import tmp_dir
|
|
import sage_eval
|
|
from sage.misc.sage_ostools import have_program
|
|
from sage.misc.cachefunc import cached_function, cached_method
|
|
@@ -1917,8 +1917,10 @@ class MathJax:
|
|
- ``locals`` - extra local variables used when
|
|
evaluating Sage code in ``x``.
|
|
|
|
- - ``mode`` - string (optional, default ``'display'``): ``'display'``
|
|
- for displaymath or ``'inline'`` for inline math
|
|
+ - ``mode`` - string (optional, default ``'display'``):
|
|
+ ``'display'`` for displaymath, ``'inline'`` for inline
|
|
+ math, or ``'plain'`` for just the LaTeX code without the
|
|
+ surrounding html and script tags.
|
|
|
|
- ``combine_all`` - boolean (Default: ``False``): If ``combine_all`` is
|
|
``True`` and the input is a tuple, then it does not return a tuple
|
|
@@ -1987,24 +1989,21 @@ class MathJax:
|
|
subparts.append(spacer % "x")
|
|
subparts.append(part[closing + 1:])
|
|
parts[i] = "".join(subparts)
|
|
- x = "".join(parts)
|
|
-
|
|
- # In MathJax:
|
|
- # inline math: <script type="math/tex">...</script>
|
|
- # displaymath: <script type="math/tex; mode=display">...</script>
|
|
from sage.misc.latex_macros import sage_configurable_latex_macros
|
|
+ latex_string = ''.join(
|
|
+ sage_configurable_latex_macros +
|
|
+ [_Latex_prefs._option['macros']] +
|
|
+ parts
|
|
+ )
|
|
if mode == 'display':
|
|
- modecode = '; mode=display'
|
|
+ html = '<html><script type="math/tex; mode=display">{0}</script></html>'
|
|
elif mode == 'inline':
|
|
- modecode = ''
|
|
+ html = '<html><script type="math/tex">{0}</script></html>'
|
|
+ elif mode == 'plain':
|
|
+ return latex_string
|
|
else:
|
|
- # what happened here?
|
|
- raise ValueError("mode must be either 'display' or 'inline'")
|
|
-
|
|
- return MathJaxExpr('<html><script type="math/tex{}">'.format(modecode)
|
|
- + ''.join(sage_configurable_latex_macros)
|
|
- + _Latex_prefs._option['macros']
|
|
- + '{}</script></html>'.format(x))
|
|
+ raise ValueError("mode must be either 'display', 'inline', or 'plain'")
|
|
+ return MathJaxExpr(html.format(latex_string))
|
|
|
|
def view(objects, title='Sage', debug=False, sep='', tiny=False,
|
|
pdflatex=None, engine=None, viewer = None, tightpage = None,
|
|
@@ -2177,6 +2176,7 @@ def view(objects, title='Sage', debug=False, sep='', tiny=False,
|
|
print(MathJax().eval(objects, mode=mode, combine_all=combine_all))
|
|
else:
|
|
base_dir = os.path.abspath("")
|
|
+ from sage.misc.temporary_file import graphics_filename
|
|
png_file = graphics_filename()
|
|
png_link = "cell://" + png_file
|
|
png(objects, os.path.join(base_dir, png_file),
|
|
@@ -2478,13 +2478,14 @@ def pretty_print_default(enable=True):
|
|
|
|
sage: pretty_print_default(True)
|
|
sage: 'foo'
|
|
- <html><script type="math/tex">\newcommand{\Bold}[1]{\mathbf{#1}}\verb|foo|</script></html>
|
|
+ \newcommand{\Bold}[1]{\mathbf{#1}}\verb|foo|
|
|
sage: pretty_print_default(False)
|
|
sage: 'foo'
|
|
'foo'
|
|
"""
|
|
- import sys
|
|
- sys.displayhook.formatter.set_display('typeset' if enable else 'simple')
|
|
+ from sage.repl.rich_output import get_display_manager
|
|
+ dm = get_display_manager()
|
|
+ dm.preferences.text = 'latex' if enable else None
|
|
|
|
|
|
common_varnames = ['alpha',
|
|
diff --git a/src/sage/misc/sage_input.py b/src/sage/misc/sage_input.py
|
|
index 157625d..130d4d2 100644
|
|
--- a/src/sage/misc/sage_input.py
|
|
+++ b/src/sage/misc/sage_input.py
|
|
@@ -1474,19 +1474,20 @@ class SageInputExpression(object):
|
|
"""
|
|
return SIE_getattr(self._sie_builder, self, attr)
|
|
|
|
- def _graphics_(self, **kwds):
|
|
+ def _rich_repr_(self, display_manager, **kwds):
|
|
"""
|
|
- Disable graphical output.
|
|
+ Disable rich output.
|
|
|
|
This is necessary because otherwise our :meth:`__getattr__`
|
|
would be called.
|
|
|
|
EXAMPLES::
|
|
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
sage: from sage.misc.sage_input import SageInputBuilder
|
|
sage: sib = SageInputBuilder()
|
|
sage: sie = sib.name('x')
|
|
- sage: sie._graphics_() is None
|
|
+ sage: sie._rich_repr_(get_display_manager()) is None
|
|
True
|
|
"""
|
|
return None
|
|
diff --git a/src/sage/misc/temporary_file.py b/src/sage/misc/temporary_file.py
|
|
index 1e936a7..6424a73 100644
|
|
--- a/src/sage/misc/temporary_file.py
|
|
+++ b/src/sage/misc/temporary_file.py
|
|
@@ -149,6 +149,10 @@ def tmp_filename(name="tmp_", ext=""):
|
|
|
|
def graphics_filename(ext='.png'):
|
|
"""
|
|
+ Deprecated SageNB graphics filename
|
|
+
|
|
+ You should just use :meth:`tmp_filename`.
|
|
+
|
|
When run from the Sage notebook, return the next available canonical
|
|
filename for a plot/graphics file in the current working directory.
|
|
Otherwise, return a temporary file inside ``SAGE_TMP``.
|
|
@@ -191,11 +195,11 @@ def graphics_filename(ext='.png'):
|
|
sage: fn.endswith('.jpeg')
|
|
True
|
|
"""
|
|
+ import sage.plot.plot
|
|
+ from sage.misc.superseded import deprecation
|
|
if ext[0] not in '.-':
|
|
- from sage.misc.superseded import deprecation
|
|
deprecation(16640, "extension must now include the dot")
|
|
ext = '.' + ext
|
|
- import sage.plot.plot
|
|
if sage.plot.plot.EMBEDDED_MODE:
|
|
# Don't use this unsafe function except in the notebook, #15515
|
|
i = 0
|
|
@@ -204,6 +208,7 @@ def graphics_filename(ext='.png'):
|
|
filename = 'sage%d%s'%(i,ext)
|
|
return filename
|
|
else:
|
|
+ deprecation(17234,'use tmp_filename instead')
|
|
return tmp_filename(ext=ext)
|
|
|
|
|
|
diff --git a/src/sage/misc/viewer.py b/src/sage/misc/viewer.py
|
|
index b012cc8..1a1fec3 100644
|
|
--- a/src/sage/misc/viewer.py
|
|
+++ b/src/sage/misc/viewer.py
|
|
@@ -25,6 +25,9 @@ Functions and classes
|
|
---------------------
|
|
"""
|
|
|
|
+from sage.structure.sage_object import SageObject
|
|
+
|
|
+
|
|
VIEWERS = ['browser', 'dvi_viewer', 'pdf_viewer', 'png_viewer']
|
|
|
|
def default_viewer(viewer=None):
|
|
@@ -136,12 +139,10 @@ def default_viewer(viewer=None):
|
|
raise ValueError('Unknown type of viewer: {}.'.format(viewer))
|
|
|
|
|
|
-from sage.structure.sage_object import SageObject
|
|
-
|
|
# _viewer_prefs: a dictionary holding global preferences for viewers.
|
|
_viewer_prefs = {}
|
|
|
|
-class Viewer:
|
|
+class Viewer(SageObject):
|
|
"""
|
|
Set defaults for various viewing applications: a web browser, a
|
|
dvi viewer, a pdf viewer, and a png viewer.
|
|
diff --git a/src/sage/plot/animate.py b/src/sage/plot/animate.py
|
|
index 75db15e..d502221 100644
|
|
--- a/src/sage/plot/animate.py
|
|
+++ b/src/sage/plot/animate.py
|
|
@@ -31,7 +31,7 @@ The sine function::
|
|
|
|
sage: sines = [plot(c*sin(x), (-2*pi,2*pi), color=Color(c,0,0), ymin=-1, ymax=1) for c in sxrange(0,1,.2)]
|
|
sage: a = animate(sines)
|
|
- sage: a
|
|
+ sage: a # optional -- ImageMagick
|
|
Animation with 5 frames
|
|
sage: a.show() # optional -- ImageMagick
|
|
|
|
@@ -43,12 +43,14 @@ Animate using FFmpeg_ instead of ImageMagick::
|
|
Animate as an APNG_::
|
|
|
|
sage: a.apng() # long time
|
|
+ doctest:...: DeprecationWarning: use tmp_filename instead
|
|
+ See http://trac.sagemath.org/17234 for details.
|
|
|
|
An animated :class:`sage.plot.graphics.GraphicsArray` of rotating ellipses::
|
|
|
|
sage: E = animate((graphics_array([[ellipse((0,0),a,b,angle=t,xmin=-3,xmax=3)+circle((0,0),3,color='blue') for a in range(1,3)] for b in range(2,4)]) for t in sxrange(0,pi/4,.15)))
|
|
- sage: E # animations produced from a generator do not have a known length
|
|
- Animation with unknown number of frames
|
|
+ sage: str(E) # animations produced from a generator do not have a known length
|
|
+ 'Animation with unknown number of frames'
|
|
sage: E.show() # optional -- ImageMagick
|
|
|
|
A simple animation of a circle shooting up to the right::
|
|
@@ -116,7 +118,7 @@ import struct
|
|
import zlib
|
|
|
|
from sage.structure.sage_object import SageObject
|
|
-from sage.misc.temporary_file import tmp_dir, graphics_filename
|
|
+from sage.misc.temporary_file import tmp_dir, tmp_filename, graphics_filename
|
|
import plot
|
|
import sage.misc.misc
|
|
import sage.misc.viewer
|
|
@@ -159,9 +161,9 @@ class Animation(SageObject):
|
|
|
|
sage: a = animate([sin(x + float(k)) for k in srange(0,2*pi,0.3)],
|
|
....: xmin=0, xmax=2*pi, figsize=[2,1])
|
|
- sage: a
|
|
+ sage: a # optional -- ImageMagick
|
|
Animation with 21 frames
|
|
- sage: a[:5]
|
|
+ sage: a[:5] # optional -- ImageMagick
|
|
Animation with 5 frames
|
|
sage: a.show() # optional -- ImageMagick
|
|
sage: a[:5].show() # optional -- ImageMagick
|
|
@@ -203,8 +205,9 @@ class Animation(SageObject):
|
|
Do not convert input iterator to a list, but ensure that
|
|
the frame count is known after rendering the frames::
|
|
|
|
- sage: a = animate((plot(x^p, (x,0,2)) for p in sxrange(1,2,.1))); a
|
|
- Animation with unknown number of frames
|
|
+ sage: a = animate((plot(x^p, (x,0,2)) for p in sxrange(1,2,.1)))
|
|
+ sage: str(a)
|
|
+ 'Animation with unknown number of frames'
|
|
sage: a.png() # long time
|
|
'.../'
|
|
sage: len(a) # long time
|
|
@@ -223,7 +226,7 @@ class Animation(SageObject):
|
|
|
|
sage: a = animate([sin(x + float(k)) for k in srange(0,2*pi,0.3)],
|
|
....: xmin=0, xmax=2*pi, figsize=[2,1]) # indirect doctest
|
|
- sage: a
|
|
+ sage: a # optional -- ImageMagick
|
|
Animation with 21 frames
|
|
"""
|
|
self._frames = v
|
|
@@ -273,12 +276,12 @@ class Animation(SageObject):
|
|
|
|
sage: a = animate([circle((i,-i), 1-1/(i+1), hue=i/10) for i in srange(0,2,0.2)],
|
|
....: xmin=0,ymin=-2,xmax=2,ymax=0,figsize=[2,2])
|
|
- sage: a
|
|
+ sage: a # optional -- ImageMagick
|
|
Animation with 10 frames
|
|
sage: frame2 = a[2] # indirect doctest
|
|
sage: frame2.show()
|
|
sage: a.show() # optional -- ImageMagick
|
|
- sage: a[3:7] # indirect doctest
|
|
+ sage: a[3:7] # optional -- ImageMagick # indirect doctest
|
|
Animation with 4 frames
|
|
sage: a[3:7].show() # optional -- ImageMagick
|
|
"""
|
|
@@ -295,7 +298,7 @@ class Animation(SageObject):
|
|
|
|
sage: a = animate([circle((i,-i), 1-1/(i+1), hue=i/10) for i in srange(0,2,0.2)],
|
|
....: xmin=0,ymin=-2,xmax=2,ymax=0,figsize=[2,2])
|
|
- sage: a
|
|
+ sage: a # optional -- ImageMagick
|
|
Animation with 10 frames
|
|
sage: a._repr_()
|
|
'Animation with 10 frames'
|
|
@@ -478,7 +481,7 @@ class Animation(SageObject):
|
|
sage: E = EllipticCurve('37a')
|
|
sage: v = [E.change_ring(GF(p)).plot(pointsize=30) for p in [97, 101, 103, 107]]
|
|
sage: a = animate(v, xmin=0, ymin=0, axes=False)
|
|
- sage: a
|
|
+ sage: a # optional -- ImageMagick
|
|
Animation with 4 frames
|
|
sage: a.show() # optional -- ImageMagick
|
|
|
|
@@ -618,19 +621,48 @@ the animate command can be saved in PNG image format.
|
|
See www.imagemagick.org and www.ffmpeg.org for more information."""
|
|
raise OSError(msg)
|
|
|
|
+ def _rich_repr_(self, display_manager, **kwds):
|
|
+ """
|
|
+ Rich Output Magic Method
|
|
+
|
|
+ See :mod:`sage.repl.rich_output` for details.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: a = animate([plot(x^2 + n) for n in range(4)], ymin=0, ymax=4)
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: a._rich_repr_(dm) # optional -- ImageMagick
|
|
+ OutputImageGif container
|
|
+ """
|
|
+ OutputImageGif = display_manager.types.OutputImageGif
|
|
+ if OutputImageGif not in display_manager.supported_output():
|
|
+ return
|
|
+ return display_manager.graphics_from_save(
|
|
+ self.save, kwds, '.gif', OutputImageGif)
|
|
+
|
|
def show(self, delay=20, iterations=0):
|
|
r"""
|
|
- Show this animation.
|
|
+ Show this animation immediately.
|
|
|
|
- INPUT:
|
|
+ This method attempts to display the graphics immediately,
|
|
+ without waiting for the currently running code (if any) to
|
|
+ return to the command line. Be careful, calling it from within
|
|
+ a loop will potentially launch a large number of external
|
|
+ viewer programs.
|
|
|
|
+ INPUT:
|
|
|
|
- - ``delay`` - (default: 20) delay in hundredths of a
|
|
+ - ``delay`` -- (default: 20) delay in hundredths of a
|
|
second between frames
|
|
|
|
- - ``iterations`` - integer (default: 0); number of
|
|
+ - ``iterations`` -- integer (default: 0); number of
|
|
iterations of animation. If 0, loop forever.
|
|
|
|
+ OUTPUT:
|
|
+
|
|
+ This method does not return anything. Use :meth:`save` if you
|
|
+ want to save the figure as an image.
|
|
|
|
.. note::
|
|
|
|
@@ -667,11 +699,9 @@ See www.imagemagick.org and www.ffmpeg.org for more information."""
|
|
|
|
See www.imagemagick.org and www.ffmpeg.org for more information.
|
|
"""
|
|
- filename = graphics_filename(ext='.gif')
|
|
- self.gif(savefile=filename, delay=delay, iterations=iterations)
|
|
- if not (sage.doctest.DOCTEST_MODE or plot.EMBEDDED_MODE):
|
|
- os.system('%s %s 2>/dev/null 1>/dev/null &'%(
|
|
- sage.misc.viewer.browser(), filename))
|
|
+ from sage.repl.rich_output import get_display_manager
|
|
+ dm = get_display_manager()
|
|
+ dm.display_immediately(self, delay=delay, iterations=iterations)
|
|
|
|
def _have_ffmpeg(self):
|
|
"""
|
|
diff --git a/src/sage/plot/circle.py b/src/sage/plot/circle.py
|
|
index 9fb6e93..9a331e0 100644
|
|
--- a/src/sage/plot/circle.py
|
|
+++ b/src/sage/plot/circle.py
|
|
@@ -190,7 +190,7 @@ class Circle(GraphicPrimitive):
|
|
sage: c = C[0]
|
|
sage: d = c.plot3d(z=2)
|
|
sage: d.texture.opacity
|
|
- 0.300000000000000
|
|
+ 0.3
|
|
|
|
::
|
|
|
|
diff --git a/src/sage/plot/disk.py b/src/sage/plot/disk.py
|
|
index 7908d2c..3e6715e 100644
|
|
--- a/src/sage/plot/disk.py
|
|
+++ b/src/sage/plot/disk.py
|
|
@@ -202,7 +202,7 @@ class Disk(GraphicPrimitive):
|
|
sage: D = disk((2,3), 1, (pi/4,pi/3), hue=.8, alpha=.3, fill=True)
|
|
sage: d = D[0]
|
|
sage: d.plot3d(z=2).texture.opacity
|
|
- 0.300000000000000
|
|
+ 0.3
|
|
|
|
::
|
|
|
|
diff --git a/src/sage/plot/graphics.py b/src/sage/plot/graphics.py
|
|
index e0fef74..0c8f61a 100644
|
|
--- a/src/sage/plot/graphics.py
|
|
+++ b/src/sage/plot/graphics.py
|
|
@@ -29,9 +29,8 @@ AUTHORS:
|
|
import os
|
|
import sage.misc.misc
|
|
from sage.misc.html import html
|
|
-from sage.misc.temporary_file import tmp_filename, graphics_filename
|
|
+from sage.misc.temporary_file import tmp_filename
|
|
from sage.structure.sage_object import SageObject
|
|
-from sage.structure.graphics_file import GraphicsFile
|
|
from sage.misc.decorators import suboptions
|
|
from colors import rgbcolor
|
|
|
|
@@ -132,7 +131,7 @@ class Graphics(SageObject):
|
|
sage: isinstance(g2, Graphics)
|
|
True
|
|
|
|
- .. automethod:: _graphics_
|
|
+ .. automethod:: _rich_repr_
|
|
"""
|
|
|
|
def __init__(self):
|
|
@@ -822,55 +821,44 @@ class Graphics(SageObject):
|
|
"""
|
|
return self.__str__()
|
|
|
|
- def _graphics_(self, mime_types=None, figsize=None, dpi=None):
|
|
+ def _rich_repr_(self, display_manager, **kwds):
|
|
"""
|
|
- Magic graphics method.
|
|
+ Rich Output Magic Method
|
|
|
|
- The presence of this method is used by the displayhook to
|
|
- decide that we want to see a graphical output by default.
|
|
-
|
|
- INPUT:
|
|
-
|
|
- - ``mime_types`` -- set of mime types (as strings).
|
|
-
|
|
- - ``figsize`` -- pair of integers (optional). The desired
|
|
- graphics size in pixels. Suggested, but need not be
|
|
- respected by the output.
|
|
-
|
|
- - ``dpi`` -- integer (optional). The desired resolution in
|
|
- dots per inch. Suggested, but need not be respected by the
|
|
- output.
|
|
-
|
|
- OUTPUT:
|
|
-
|
|
- Return an instance of
|
|
- :class:`sage.structure.graphics_file.GraphicsFile`
|
|
- encapsulating a suitable image file. If ``mime_types`` is
|
|
- specified, the resulting file format must match one of these.
|
|
-
|
|
- Alternatively, this method can return ``None`` to indicate
|
|
- that textual representation is preferable and/or no graphics
|
|
- with the desired mime type can be generated.
|
|
+ See :mod:`sage.repl.rich_output` for details.
|
|
|
|
EXAMPLES::
|
|
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
sage: g = Graphics()
|
|
- sage: g._graphics_()
|
|
- Graphics file image/png
|
|
-
|
|
- sage: [g, g]
|
|
- [Graphics object consisting of 0 graphics primitives,
|
|
- Graphics object consisting of 0 graphics primitives]
|
|
-
|
|
- sage: g._graphics_(mime_types={'foo/bar'}) is None
|
|
- True
|
|
- """
|
|
- from sage.structure.graphics_file import Mime, graphics_from_save
|
|
- preference = [Mime.PNG, Mime.SVG, Mime.JPG, Mime.PDF]
|
|
- return graphics_from_save(self.save, preference,
|
|
- allowed_mime_types=mime_types,
|
|
- figsize=figsize, dpi=dpi)
|
|
-
|
|
+ sage: g._rich_repr_(dm)
|
|
+ OutputImagePng container
|
|
+ """
|
|
+ types = display_manager.types
|
|
+ prefer_raster = (
|
|
+ ('.png', types.OutputImagePng),
|
|
+ ('.jpg', types.OutputImageJpg),
|
|
+ ('.gif', types.OutputImageGif),
|
|
+ )
|
|
+ prefer_vector = (
|
|
+ ('.svg', types.OutputImageSvg),
|
|
+ ('.pdf', types.OutputImagePdf),
|
|
+ )
|
|
+ graphics = display_manager.preferences.graphics
|
|
+ if graphics == 'disable':
|
|
+ return
|
|
+ elif graphics == 'raster' or graphics is None:
|
|
+ preferred = prefer_raster + prefer_vector
|
|
+ elif graphics == 'vector':
|
|
+ preferred = prefer_vector + prefer_raster
|
|
+ else:
|
|
+ raise ValueError('unknown graphics output preference')
|
|
+ for file_ext, output_container in preferred:
|
|
+ if output_container in display_manager.supported_output():
|
|
+ return display_manager.graphics_from_save(
|
|
+ self.save, kwds, file_ext, output_container)
|
|
+
|
|
def __str__(self):
|
|
r"""
|
|
Return string representation of this plot.
|
|
@@ -1266,8 +1254,7 @@ class Graphics(SageObject):
|
|
# NOTE: If you intend to use a new parameter in show(), you should update
|
|
# this dictionary to contain the default value for that parameter.
|
|
|
|
- SHOW_OPTIONS = dict(filename=None,
|
|
- # axes options
|
|
+ SHOW_OPTIONS = dict(# axes options
|
|
axes=None, axes_labels=None, axes_pad=None,
|
|
base=None, scale=None,
|
|
xmin=None, xmax=None, ymin=None, ymax=None,
|
|
@@ -1298,11 +1285,15 @@ class Graphics(SageObject):
|
|
shadow=False, title=None)
|
|
def show(self, filename=None, linkmode=False, **kwds):
|
|
r"""
|
|
- Show this graphics image with the default image viewer.
|
|
+ Show this graphics image immediately.
|
|
|
|
- OPTIONAL INPUT:
|
|
+ This method attempts to display the graphics immediately,
|
|
+ without waiting for the currently running code (if any) to
|
|
+ return to the command line. Be careful, calling it from within
|
|
+ a loop will potentially launch a large number of external
|
|
+ viewer programs.
|
|
|
|
- - ``filename`` - (default: None) string
|
|
+ OPTIONAL INPUT:
|
|
|
|
- ``dpi`` - (default: 100) dots per inch
|
|
|
|
@@ -1368,9 +1359,6 @@ class Graphics(SageObject):
|
|
rendering of the grid lines, the horizontal grid lines or the
|
|
vertical grid lines, respectively.
|
|
|
|
- - ``linkmode`` - (default: False) If True a string containing a link
|
|
- to the produced file is returned.
|
|
-
|
|
- ``transparent`` - (default: False) If True, make the background transparent.
|
|
|
|
- ``axes_pad`` - (default: 0.02 on ``"linear"`` scale, 1 on
|
|
@@ -1532,6 +1520,11 @@ class Graphics(SageObject):
|
|
in the figure. This requires LaTeX, dvipng and Ghostscript to
|
|
be installed.
|
|
|
|
+ OUTPUT:
|
|
+
|
|
+ This method does not return anything. Use :meth:`save` if you
|
|
+ want to save the figure as an image.
|
|
+
|
|
EXAMPLES::
|
|
|
|
sage: c = circle((1,1), 1, color='red')
|
|
@@ -1950,21 +1943,34 @@ class Graphics(SageObject):
|
|
...
|
|
ValueError: figsize should be a positive number or a list of two positive numbers, not [2, 3, 4]
|
|
sage: P.show(figsize=[sqrt(2),sqrt(3)])
|
|
- """
|
|
- if filename is None:
|
|
- filename = graphics_filename()
|
|
|
|
- self.save(filename, **kwds)
|
|
+ TESTS::
|
|
|
|
- if sage.plot.plot.EMBEDDED_MODE:
|
|
+ sage: P = plot(x^2,(x,0,1))
|
|
+ sage: P.show(linkmode=True)
|
|
+ doctest:...: DeprecationWarning: the filename and linkmode arguments are deprecated, use save() to save
|
|
+ See http://trac.sagemath.org/17234 for details.
|
|
+ doctest:...: DeprecationWarning: use tmp_filename instead
|
|
+ See http://trac.sagemath.org/17234 for details.
|
|
+ "<img src='cell:///...png'>"
|
|
+ """
|
|
+ if filename or linkmode:
|
|
+ from sage.misc.superseded import deprecation
|
|
+ deprecation(17234,'the filename and linkmode arguments are deprecated, '
|
|
+ 'use save() to save')
|
|
+ if filename is None:
|
|
+ from sage.misc.temporary_file import graphics_filename
|
|
+ filename = graphics_filename()
|
|
+ self.save(filename, **kwds)
|
|
if linkmode:
|
|
return "<img src='cell://%s'>" % filename
|
|
else:
|
|
html("<img src='cell://%s'>" % filename)
|
|
return
|
|
-
|
|
- gfx = GraphicsFile(filename, mime_type='image/png')
|
|
- gfx.launch_viewer()
|
|
+
|
|
+ from sage.repl.rich_output import get_display_manager
|
|
+ dm = get_display_manager()
|
|
+ dm.display_immediately(self, **kwds)
|
|
|
|
def xmin(self, xmin=None):
|
|
"""
|
|
@@ -2418,9 +2424,10 @@ class Graphics(SageObject):
|
|
``typeset`` must not be set to an arbitrary string::
|
|
|
|
sage: plot(x, typeset='garbage')
|
|
- doctest:...: FormatterWarning: Exception in text/plain formatter:
|
|
- typeset must be set to one of 'default', 'latex', or 'type1'; got 'garbage'.
|
|
- None
|
|
+ doctest:...: RichReprWarning: Exception in _rich_repr_ while
|
|
+ displaying object: typeset must be set to one of 'default',
|
|
+ 'latex', or 'type1'; got 'garbage'.
|
|
+ Graphics object consisting of 1 graphics primitive
|
|
|
|
We verify that numerical options are changed to float before saving (:trac:`14741`).
|
|
By default, Sage 5.10 changes float objects to the `RealLiteral` type.
|
|
@@ -2948,9 +2955,8 @@ class Graphics(SageObject):
|
|
|
|
INPUT:
|
|
|
|
- - ``filename`` -- a string (default: autogenerated), the filename and
|
|
- the image format given by the extension, which can be one of the
|
|
- following:
|
|
+ - ``filename`` -- string. The filename and the image format
|
|
+ given by the extension, which can be one of the following:
|
|
|
|
* ``.eps``,
|
|
|
|
@@ -3036,12 +3042,10 @@ class Graphics(SageObject):
|
|
fig_tight = options.pop('fig_tight')
|
|
|
|
if filename is None:
|
|
- try:
|
|
- filename = options.pop('filename')
|
|
- except KeyError:
|
|
- # Put this in except (not in pop()) such that the file is
|
|
- # only created when needed.
|
|
- filename = graphics_filename()
|
|
+ from sage.misc.superseded import deprecation
|
|
+ deprecation(17234,'the filename argument is now mandatory')
|
|
+ from sage.misc.temporary_file import graphics_filename
|
|
+ filename = graphics_filename()
|
|
ext = os.path.splitext(filename)[1].lower()
|
|
|
|
if ext not in ALLOWED_EXTENSIONS:
|
|
@@ -3113,7 +3117,7 @@ class GraphicsArray(SageObject):
|
|
GraphicsArray takes a (`m` x `n`) list of lists of
|
|
graphics objects and plots them all on one canvas.
|
|
|
|
- .. automethod:: _graphics_
|
|
+ .. automethod:: _rich_repr_
|
|
"""
|
|
def __init__(self, array):
|
|
"""
|
|
@@ -3186,26 +3190,43 @@ class GraphicsArray(SageObject):
|
|
"""
|
|
return self.__str__()
|
|
|
|
- def _graphics_(self, mime_types=None, figsize=None, dpi=None):
|
|
+ def _rich_repr_(self, display_manager, **kwds):
|
|
"""
|
|
- Magic graphics method.
|
|
+ Rich Output Magic Method
|
|
|
|
- See :meth:`sage.plot.graphics.Graphics._graphics_` for details.
|
|
+ See :mod:`sage.repl.rich_output` for details.
|
|
|
|
EXAMPLES::
|
|
|
|
- sage: from sage.plot.graphics import GraphicsArray
|
|
- sage: g = GraphicsArray([])
|
|
- sage: g._graphics_()
|
|
- Graphics file image/png
|
|
- sage: g._graphics_(mime_types={'foo/bar'}) is None
|
|
- True
|
|
- """
|
|
- from sage.structure.graphics_file import Mime, graphics_from_save
|
|
- preference = [Mime.PNG, Mime.SVG, Mime.JPG, Mime.PDF]
|
|
- return graphics_from_save(self.save, preference,
|
|
- allowed_mime_types=mime_types,
|
|
- figsize=figsize, dpi=dpi)
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: g = graphics_array([Graphics(), Graphics()], 1, 2)
|
|
+ sage: g._rich_repr_(dm)
|
|
+ OutputImagePng container
|
|
+ """
|
|
+ types = display_manager.types
|
|
+ prefer_raster = (
|
|
+ ('.png', types.OutputImagePng),
|
|
+ ('.jpg', types.OutputImageJpg),
|
|
+ ('.gif', types.OutputImageGif),
|
|
+ )
|
|
+ prefer_vector = (
|
|
+ ('.svg', types.OutputImageSvg),
|
|
+ ('.pdf', types.OutputImagePdf),
|
|
+ )
|
|
+ graphics = display_manager.preferences.graphics
|
|
+ if graphics == 'disable':
|
|
+ return
|
|
+ elif graphics == 'raster' or graphics is None:
|
|
+ preferred = prefer_raster + prefer_vector
|
|
+ elif graphics == 'vector':
|
|
+ preferred = prefer_vector + prefer_raster
|
|
+ else:
|
|
+ raise ValueError('unknown graphics output preference')
|
|
+ for file_ext, output_container in preferred:
|
|
+ if output_container in display_manager.supported_output():
|
|
+ return display_manager.graphics_from_save(
|
|
+ self.save, kwds, file_ext, output_container)
|
|
|
|
def __str__(self):
|
|
"""
|
|
@@ -3362,17 +3383,6 @@ class GraphicsArray(SageObject):
|
|
"""
|
|
return len(self._glist)
|
|
|
|
-# This does not work, and can never have worked!
|
|
-# To make this work, one would also change the
|
|
-# dimensions of the array, but it's not clear there
|
|
-# is a canonical way to do this.
|
|
-#
|
|
-# def append(self, g):
|
|
-# """
|
|
-# Appends a graphic to the array.
|
|
-# """
|
|
-# self._glist.append(g)
|
|
-
|
|
def append(self, g):
|
|
"""
|
|
Appends a graphic to the array. Currently
|
|
@@ -3387,20 +3397,32 @@ class GraphicsArray(SageObject):
|
|
...
|
|
NotImplementedError: Appending to a graphics array is not yet implemented
|
|
"""
|
|
+ # Not clear if there is a way to do this
|
|
raise NotImplementedError('Appending to a graphics array is not yet implemented')
|
|
|
|
-
|
|
def save(self, filename=None, dpi=DEFAULT_DPI, figsize=None, axes=None,
|
|
**kwds):
|
|
r"""
|
|
- Save the ``graphics_array`` to a png called ``filename``.
|
|
-
|
|
- We loop over all graphics objects in the array and add them to
|
|
- a subplot and then render that.
|
|
+ Save the graphics array.
|
|
|
|
INPUT:
|
|
|
|
- - ``filename`` - (default: None) string
|
|
+ - ``filename`` -- string. The filename and the image format
|
|
+ given by the extension, which can be one of the following:
|
|
+
|
|
+ * ``.eps``,
|
|
+
|
|
+ * ``.pdf``,
|
|
+
|
|
+ * ``.png``,
|
|
+
|
|
+ * ``.ps``,
|
|
+
|
|
+ * ``.sobj`` (for a Sage object you can load later),
|
|
+
|
|
+ * ``.svg``,
|
|
+
|
|
+ * empty extension will be treated as ``.sobj``.
|
|
|
|
- ``dpi`` - dots per inch
|
|
|
|
@@ -3418,12 +3440,15 @@ class GraphicsArray(SageObject):
|
|
|
|
TESTS::
|
|
|
|
- sage: graphics_array([]).save()
|
|
- sage: graphics_array([[]]).save()
|
|
+ sage: graphics_array([]).save(F)
|
|
+ sage: graphics_array([[]]).save(F)
|
|
"""
|
|
if figsize is not None:
|
|
self._set_figsize_(figsize)
|
|
if filename is None:
|
|
+ from sage.misc.superseded import deprecation
|
|
+ deprecation(17234,'the filename argument is now mandatory')
|
|
+ from sage.misc.temporary_file import graphics_filename
|
|
filename = graphics_filename()
|
|
|
|
#glist is a list of Graphics objects:
|
|
@@ -3444,7 +3469,7 @@ class GraphicsArray(SageObject):
|
|
g.matplotlib(filename, figure=figure, sub=subplot,
|
|
verify=do_verify, axes = axes, **kwds)
|
|
g.save(filename, dpi=dpi, figure=figure, sub=subplot,
|
|
- verify=do_verify, axes = axes, **kwds)
|
|
+ verify=do_verify, axes=axes, **kwds)
|
|
|
|
def save_image(self, filename=None, *args, **kwds):
|
|
r"""
|
|
@@ -3464,19 +3489,21 @@ class GraphicsArray(SageObject):
|
|
|
|
sage: plots = [[plot(m*cos(x + n*pi/4), (x,0, 2*pi)) for n in range(3)] for m in range(1,3)]
|
|
sage: G = graphics_array(plots)
|
|
- sage: G.save_image(tmp_filename()+'.png')
|
|
+ sage: G.save_image(tmp_filename(ext='.png'))
|
|
"""
|
|
self.save(filename, *args, **kwds)
|
|
|
|
-
|
|
- def show(self, filename=None, dpi=DEFAULT_DPI, figsize=None,
|
|
- axes=None, **kwds):
|
|
+ def show(self, **kwds):
|
|
r"""
|
|
- Show this graphics array using the default viewer.
|
|
+ Show this graphics array immediately.
|
|
|
|
- OPTIONAL INPUT:
|
|
+ This method attempts to display the graphics immediately,
|
|
+ without waiting for the currently running code (if any) to
|
|
+ return to the command line. Be careful, calling it from within
|
|
+ a loop will potentially launch a large number of external
|
|
+ viewer programs.
|
|
|
|
- - ``filename`` - (default: None) string
|
|
+ OPTIONAL INPUT:
|
|
|
|
- ``dpi`` - dots per inch
|
|
|
|
@@ -3491,6 +3518,11 @@ class GraphicsArray(SageObject):
|
|
- ``frame`` - (default: False) draw a frame around the
|
|
image
|
|
|
|
+ OUTPUT:
|
|
+
|
|
+ This method does not return anything. Use :meth:`save` if you
|
|
+ want to save the figure as an image.
|
|
+
|
|
EXAMPLES:
|
|
|
|
This draws a graphics array with four trig plots and no
|
|
@@ -3499,8 +3531,6 @@ class GraphicsArray(SageObject):
|
|
sage: G = graphics_array([[plot(sin), plot(cos)], [plot(tan), plot(sec)]])
|
|
sage: G.show(axes=False)
|
|
"""
|
|
- if filename is None:
|
|
- filename = graphics_filename()
|
|
- self.save(filename, dpi=dpi, figsize=figsize, axes=axes, **kwds)
|
|
- gfx = GraphicsFile(filename, mime_type='image/png')
|
|
- gfx.launch_viewer()
|
|
+ from sage.repl.rich_output import get_display_manager
|
|
+ dm = get_display_manager()
|
|
+ dm.display_immediately(self, **kwds)
|
|
diff --git a/src/sage/plot/line.py b/src/sage/plot/line.py
|
|
index 61694b1..e164e0c 100644
|
|
--- a/src/sage/plot/line.py
|
|
+++ b/src/sage/plot/line.py
|
|
@@ -99,7 +99,7 @@ class Line(GraphicPrimitive_xydata):
|
|
Line defined by 4 points
|
|
sage: m=l.plot3d(z=2)
|
|
sage: m.texture.opacity
|
|
- 0.500000000000000
|
|
+ 0.5
|
|
sage: m.thickness
|
|
10
|
|
sage: L = line([(1,1), (1,2), (2,2), (2,1)], linestyle=":")
|
|
diff --git a/src/sage/plot/plot3d/base.pyx b/src/sage/plot/plot3d/base.pyx
|
|
index 335bb43..8372346 100644
|
|
--- a/src/sage/plot/plot3d/base.pyx
|
|
+++ b/src/sage/plot/plot3d/base.pyx
|
|
@@ -40,11 +40,12 @@ from cStringIO import StringIO
|
|
|
|
from sage.misc.misc import sage_makedirs
|
|
from sage.env import SAGE_LOCAL
|
|
+from sage.doctest import DOCTEST_MODE
|
|
|
|
from sage.modules.free_module_element import vector
|
|
|
|
from sage.rings.real_double import RDF
|
|
-from sage.misc.temporary_file import tmp_filename, graphics_filename
|
|
+from sage.misc.temporary_file import tmp_filename
|
|
from texture import Texture, is_Texture
|
|
from transform cimport Transformation, point_c, face_c
|
|
include "point_c.pxi"
|
|
@@ -63,8 +64,8 @@ cdef class Graphics3d(SageObject):
|
|
"""
|
|
This is the baseclass for all 3d graphics objects.
|
|
|
|
- .. automethod:: _graphics_
|
|
.. automethod:: __add__
|
|
+ .. automethod:: _rich_repr_
|
|
"""
|
|
def __cinit__(self):
|
|
"""
|
|
@@ -94,34 +95,32 @@ cdef class Graphics3d(SageObject):
|
|
"""
|
|
return str(self)
|
|
|
|
- def _graphics_(self, mime_types=None, figsize=None, dpi=None):
|
|
+ def _rich_repr_(self, display_manager, **kwds):
|
|
"""
|
|
- Magic graphics method.
|
|
+ Rich Output Magic Method
|
|
|
|
- The presence of this method is used by the displayhook to
|
|
- decide that we want to see a graphical output by default.
|
|
-
|
|
- INPUT/OUTPUT:
|
|
-
|
|
- See :meth:`sage.plot.graphics.Graphics._graphics_` for details.
|
|
+ See :mod:`sage.repl.rich_output` for details.
|
|
|
|
EXAMPLES::
|
|
|
|
- sage: S = sphere((0, 0, 0), 1)
|
|
- sage: S._graphics_(mime_types={'image/png'})
|
|
- Graphics file image/png
|
|
- sage: S # also productes graphics
|
|
- Graphics3d Object
|
|
- sage: [S, S]
|
|
- [Graphics3d Object, Graphics3d Object]
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: g = sphere()
|
|
+ sage: g._rich_repr_(dm)
|
|
+ OutputSceneJmol container
|
|
"""
|
|
- from sage.structure.graphics_file import (
|
|
- Mime, graphics_from_save, GraphicsFile)
|
|
### First, figure out the best graphics format
|
|
- can_view_jmol = (mime_types is None) or (Mime.JMOL in mime_types)
|
|
- viewer = self._extra_kwds.get('viewer', None)
|
|
+ types = display_manager.types
|
|
+ can_view_jmol = (types.OutputSceneJmol in display_manager.supported_output())
|
|
+ can_view_canvas3d = (types.OutputSceneCanvas3d in display_manager.supported_output())
|
|
+ can_view_wavefront = (types.OutputSceneWavefront in display_manager.supported_output())
|
|
+ opts = self._process_viewing_options(kwds)
|
|
+ viewer = opts.get('viewer', None)
|
|
+ if viewer == 'java3d':
|
|
+ from sage.misc.superseded import deprecation
|
|
+ deprecation(17234, 'use viewer="wavefront" instead of "java3d"')
|
|
# make sure viewer is one of the supported options
|
|
- if viewer not in [None, 'jmol', 'tachyon']:
|
|
+ if viewer not in [None, 'jmol', 'tachyon', 'canvas3d', 'wavefront']:
|
|
import warnings
|
|
warnings.warn('viewer={0} is not supported'.format(viewer))
|
|
viewer = None
|
|
@@ -129,24 +128,198 @@ cdef class Graphics3d(SageObject):
|
|
if viewer is None:
|
|
viewer = 'jmol'
|
|
# fall back to 2d image if necessary
|
|
- if viewer == 'jmol' and not can_view_jmol:
|
|
- viewer = 'tachyon'
|
|
+ if viewer == 'canvas3d' and not can_view_canvas3d: viewer = 'jmol'
|
|
+ if viewer == 'wavefront' and not can_view_wavefront: viewer = 'jmol'
|
|
+ if viewer == 'jmol' and not can_view_jmol: viewer = 'tachyon'
|
|
### Second, return the corresponding graphics file
|
|
if viewer == 'jmol':
|
|
- filename = tmp_filename(
|
|
- ext=os.path.extsep + Mime.extension(Mime.JMOL))
|
|
- self.save(filename)
|
|
- return GraphicsFile(filename, Mime.JMOL)
|
|
+ return self._rich_repr_jmol(**kwds)
|
|
elif viewer == 'tachyon':
|
|
- preference = [Mime.PNG, Mime.JPG]
|
|
- figsize = self._extra_kwds.get('figsize', figsize)
|
|
- dpi = self._extra_kwds.get('dpi', dpi)
|
|
- return graphics_from_save(self.save, preference,
|
|
- allowed_mime_types=mime_types,
|
|
- figsize=figsize, dpi=dpi)
|
|
+ preferred = (
|
|
+ types.OutputImagePng,
|
|
+ types.OutputImageJpg,
|
|
+ types.OutputImageGif,
|
|
+ )
|
|
+ for output_container in preferred:
|
|
+ if output_container in display_manager.supported_output():
|
|
+ return self._rich_repr_tachyon(output_container, **kwds)
|
|
+ elif viewer == 'canvas3d':
|
|
+ return self._rich_repr_canvas3d(**kwds)
|
|
+ elif viewer == 'wavefront':
|
|
+ return self._rich_repr_wavefront(**kwds)
|
|
else:
|
|
assert False # unreachable
|
|
|
|
+ def _rich_repr_tachyon(self, output_container, **kwds):
|
|
+ """
|
|
+ Rich Representation using Tachyon.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``output_container`` -- the rich output container to contain
|
|
+ the rendered image (can be png, gif, or jpg). Determines the
|
|
+ type of the output.
|
|
+
|
|
+ - ``**kwds`` -- Optional keyword arguments are passed to the
|
|
+ Tachyon raytracer.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Instance of
|
|
+ :class:`~sage.repl.rich_output.output_graphics.OutputImagePng`,
|
|
+ :class:`~sage.repl.rich_output.output_graphics.OutputImageGif`, or
|
|
+ :class:`~sage.repl.rich_output.output_graphics.OutputImageJpg`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: import sage.repl.rich_output.output_catalog as catalog
|
|
+ sage: sphere()._rich_repr_tachyon(catalog.OutputImagePng)
|
|
+ OutputImagePng container
|
|
+ sage: import sage.repl.rich_output.output_catalog as catalog
|
|
+ sage: sphere()._rich_repr_tachyon(catalog.OutputImageJpg) # optional -- libjpeg
|
|
+ OutputImageJpg container
|
|
+ """
|
|
+ filename = tmp_filename(ext='.png')
|
|
+ opts = self._process_viewing_options(kwds)
|
|
+ T = self._prepare_for_tachyon(
|
|
+ opts['frame'], opts['axes'], opts['frame_aspect_ratio'],
|
|
+ opts['aspect_ratio'], opts['zoom']
|
|
+ )
|
|
+ x, y = opts['figsize'][0]*100, opts['figsize'][1]*100
|
|
+ if DOCTEST_MODE:
|
|
+ x, y = 10, 10
|
|
+ tachyon_rt(T.tachyon(), filename, opts['verbosity'],
|
|
+ '-res %s %s' % (x, y))
|
|
+ from sage.repl.rich_output.buffer import OutputBuffer
|
|
+ import sage.repl.rich_output.output_catalog as catalog
|
|
+ import PIL.Image as Image
|
|
+ if output_container is catalog.OutputImagePng:
|
|
+ buf = OutputBuffer.from_file(filename)
|
|
+ elif output_container is catalog.OutputImageGif:
|
|
+ gif = tmp_filename(ext='.gif')
|
|
+ Image.open(filename).save(gif)
|
|
+ buf = OutputBuffer.from_file(gif)
|
|
+ elif output_container is catalog.OutputImageJpg:
|
|
+ jpg = tmp_filename(ext='.jpg')
|
|
+ Image.open(filename).save(jpg)
|
|
+ buf = OutputBuffer.from_file(jpg)
|
|
+ else:
|
|
+ raise ValueError('output_container not supported')
|
|
+ return output_container(buf)
|
|
+
|
|
+ def _rich_repr_jmol(self, **kwds):
|
|
+ """
|
|
+ Rich Representation as JMol scene
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ Optional keyword arguments are passed to JMol.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Instance of
|
|
+ :class:`sage.repl.rich_output.output_graphics3d.OutputSceneJmol`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: sphere()._rich_repr_jmol()
|
|
+ OutputSceneJmol container
|
|
+ """
|
|
+ from sage.misc.temporary_file import tmp_dir
|
|
+ root_dir = os.path.abspath(tmp_dir())
|
|
+ scene_zip = os.path.join(root_dir, 'scene.spt.zip')
|
|
+ preview_png = os.path.join(root_dir, 'preview.png')
|
|
+ opts = self._process_viewing_options(kwds)
|
|
+ zoom = opts['zoom']
|
|
+ T = self._prepare_for_jmol(
|
|
+ opts['frame'],
|
|
+ opts['axes'],
|
|
+ opts['frame_aspect_ratio'],
|
|
+ opts['aspect_ratio'],
|
|
+ zoom,
|
|
+ )
|
|
+ T.export_jmol(scene_zip, zoom=zoom*100, **kwds)
|
|
+ from sage.interfaces.jmoldata import JmolData
|
|
+ jdata = JmolData()
|
|
+ if not jdata.is_jvm_available():
|
|
+ # We can only use JMol to generate preview if a jvm is installed
|
|
+ from sage.repl.rich_output.output_graphics import OutputImagePng
|
|
+ tachyon = self._rich_repr_tachyon(OutputImagePng, **kwds)
|
|
+ tachyon.png.save_as(preview_png)
|
|
+ else:
|
|
+ # Java needs absolute paths
|
|
+ script = '''set defaultdirectory "%s"\nscript SCRIPT\n''' % scene_zip
|
|
+ jdata.export_image(targetfile=preview_png, datafile=script,
|
|
+ image_type="PNG",
|
|
+ figsize=opts['figsize'])
|
|
+ from sage.repl.rich_output.output_graphics3d import OutputSceneJmol
|
|
+ from sage.repl.rich_output.buffer import OutputBuffer
|
|
+ scene_zip = OutputBuffer.from_file(scene_zip)
|
|
+ preview_png = OutputBuffer.from_file(preview_png)
|
|
+ return OutputSceneJmol(scene_zip, preview_png)
|
|
+
|
|
+ def _rich_repr_wavefront(self, **kwds):
|
|
+ r"""
|
|
+ Rich Representation as Wavefront (obj + mtl) Scene
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ Optional keyword arguments are ignored.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Instance of
|
|
+ :class:`sage.repl.rich_output.output_graphics3d.OutputSceneWavefront`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: line = line3d([(0,0,0), (1,1,1)])
|
|
+ sage: out = line._rich_repr_wavefront()
|
|
+ sage: out
|
|
+ OutputSceneWavefront container
|
|
+ sage: out.obj.get()
|
|
+ 'mtllib ... 6 2 7 11\nf 7 8 12\nf 8 9 12\nf 9 10 12\nf 10 11 12\nf 11 7 12\n'
|
|
+ sage: out.mtl.get()
|
|
+ 'newmtl texture...\nKd 0.4 0.4 1.0\nKs 0.0 0.0 0.0\nillum 1\nNs 1.0\nd 1.0\n'
|
|
+ """
|
|
+ from sage.repl.rich_output.output_graphics3d import OutputSceneWavefront
|
|
+ from sage.repl.rich_output.buffer import OutputBuffer
|
|
+ obj = OutputBuffer('mtllib scene.mtl\n' + self.obj())
|
|
+ return OutputSceneWavefront(obj, self.mtl_str())
|
|
+
|
|
+ def _rich_repr_canvas3d(self, **kwds):
|
|
+ r"""
|
|
+ Rich Representation as Canvas3D Scene
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ Optional keyword arguments.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Instance of
|
|
+ :class:`sage.repl.rich_output.output_graphics3d.OutputSceneCanvas3d`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: out = sphere()._rich_repr_canvas3d()
|
|
+ sage: out
|
|
+ OutputSceneCanvas3d container
|
|
+ sage: out.canvas3d.get()
|
|
+ "[{vertices:[{x:0,y:0,z:-1},...,color:'#6666ff'}]"
|
|
+ """
|
|
+ opts = self._process_viewing_options(kwds)
|
|
+ aspect_ratio = opts['aspect_ratio'] # this necessarily has a value now
|
|
+ frame_aspect_ratio = opts['frame_aspect_ratio']
|
|
+ zoom = opts['zoom']
|
|
+ frame = opts['frame']
|
|
+ axes = opts['axes']
|
|
+ T = self._prepare_for_tachyon(frame, axes, frame_aspect_ratio, aspect_ratio, zoom)
|
|
+ data = flatten_list(T.json_repr(T.default_render_params()))
|
|
+ canvas3d = '[' + ','.join(data) + ']'
|
|
+ from sage.repl.rich_output.output_catalog import OutputSceneCanvas3d
|
|
+ return OutputSceneCanvas3d(canvas3d)
|
|
+
|
|
def __str__(self):
|
|
"""
|
|
EXAMPLES::
|
|
@@ -505,7 +678,7 @@ cdef class Graphics3d(SageObject):
|
|
<Scene>
|
|
<Viewpoint position='0 0 6'/>
|
|
<Transform translation='1 2 3'>
|
|
- <Shape><Sphere radius='5.0'/><Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
|
|
+ <Shape><Sphere radius='5.0'/><Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1.0' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
|
|
</Transform>
|
|
</Scene>
|
|
</X3D>
|
|
@@ -522,9 +695,9 @@ cdef class Graphics3d(SageObject):
|
|
<IndexedFaceSet coordIndex='...'>
|
|
<Coordinate point='...'/>
|
|
</IndexedFaceSet>
|
|
- <Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
|
|
+ <Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1.0' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
|
|
<Transform translation='0 0 0'>
|
|
- <Shape><Sphere radius='0.5'/><Appearance><Material diffuseColor='1.0 0.0 0.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
|
|
+ <Shape><Sphere radius='0.5'/><Appearance><Material diffuseColor='1.0 0.0 0.0' shininess='1.0' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
|
|
</Transform>
|
|
</Scene>
|
|
</X3D>
|
|
@@ -561,7 +734,7 @@ cdef class Graphics3d(SageObject):
|
|
COLOR 1.0 1.0 1.0
|
|
TEXFUNC 0
|
|
Texdef texture...
|
|
- Ambient 0.333333333333 Diffuse 0.666666666667 Specular 0.0 Opacity 1
|
|
+ Ambient 0.333333333333 Diffuse 0.666666666667 Specular 0.0 Opacity 1.0
|
|
Color 1.0 1.0 0.0
|
|
TexFunc 0
|
|
Sphere center 1.0 -2.0 3.0 Rad 5.0 texture...
|
|
@@ -573,14 +746,13 @@ cdef class Graphics3d(SageObject):
|
|
begin_scene
|
|
...
|
|
Texdef texture...
|
|
- Ambient 0.333333333333 Diffuse 0.666666666667 Specular 0.0 Opacity 1
|
|
+ Ambient 0.333333333333 Diffuse 0.666666666667 Specular 0.0 Opacity 1.0
|
|
Color 1.0 1.0 0.0
|
|
TexFunc 0
|
|
TRI V0 ...
|
|
Sphere center 1.0 -2.0 3.0 Rad 0.5 texture...
|
|
end_scene
|
|
"""
|
|
-
|
|
render_params = self.default_render_params()
|
|
# switch from LH to RH coords to be consistent with java rendition
|
|
render_params.push_transform(Transformation(scale=[1,-1,1]))
|
|
@@ -888,15 +1060,15 @@ end_scene""" % (render_params.antialiasing,
|
|
Kd 1.0 1e-05 1e-05
|
|
Ks 0.0 0.0 0.0
|
|
illum 1
|
|
- Ns 1
|
|
- d 1
|
|
+ Ns 1.0
|
|
+ d 1.0
|
|
newmtl ...
|
|
Ka 0.5 0.5 5e-06
|
|
Kd 1.0 1.0 1e-05
|
|
Ks 0.0 0.0 0.0
|
|
illum 1
|
|
- Ns 1
|
|
- d 0.500000000000000
|
|
+ Ns 1.0
|
|
+ d 0.5
|
|
"""
|
|
return "\n\n".join(sorted([t.mtl_str() for t in self.texture_set()])) + "\n"
|
|
|
|
@@ -1087,8 +1259,16 @@ end_scene""" % (render_params.antialiasing,
|
|
|
|
return opts
|
|
|
|
- def show(self, *, filename=None, **kwds):
|
|
+ def show(self, **kwds):
|
|
"""
|
|
+ Display graphics immediately
|
|
+
|
|
+ This method attempts to display the graphics immediately,
|
|
+ without waiting for the currently running code (if any) to
|
|
+ return to the command line. Be careful, calling it from within
|
|
+ a loop will potentially launch a large number of external
|
|
+ viewer programs.
|
|
+
|
|
INPUT:
|
|
|
|
- ``viewer`` -- string (default: 'jmol'), how to view
|
|
@@ -1103,9 +1283,6 @@ end_scene""" % (render_params.antialiasing,
|
|
* 'canvas3d': Web-based 3D viewer powered by JavaScript and
|
|
<canvas> (notebook only)
|
|
|
|
- - ``filename`` -- string (default: a temp file); filename
|
|
- without extension to save the image file(s) to
|
|
-
|
|
- ``verbosity`` -- display information about rendering
|
|
the figure
|
|
|
|
@@ -1132,6 +1309,11 @@ end_scene""" % (render_params.antialiasing,
|
|
- ``**kwds`` -- other options, which make sense for particular
|
|
rendering engines
|
|
|
|
+ OUTPUT:
|
|
+
|
|
+ This method does not return anything. Use :meth:`save` if you
|
|
+ want to save the figure as an image.
|
|
+
|
|
CHANGING DEFAULTS: Defaults can be uniformly changed by importing a
|
|
dictionary and changing it. For example, here we change the default
|
|
so images display without a frame instead of with one::
|
|
@@ -1177,159 +1359,79 @@ end_scene""" % (render_params.antialiasing,
|
|
the plot rendered inline using HTML canvas::
|
|
|
|
sage: p.show(viewer='canvas3d')
|
|
+ """
|
|
+ from sage.repl.rich_output import get_display_manager
|
|
+ dm = get_display_manager()
|
|
+ dm.display_immediately(self, **kwds)
|
|
|
|
- From the command line, you probably want to specify an explicit
|
|
- filename::
|
|
+ def _save_image_png(self, filename, **kwds):
|
|
+ r"""
|
|
+ Save a PNG rendering.
|
|
+
|
|
+ This private method is only for use by :meth:`save_image`.
|
|
|
|
- sage: basedir = tmp_dir()
|
|
- sage: basename = os.path.join(basedir, "xyz")
|
|
- sage: p.show(filename=basename)
|
|
+ EXAMPLES::
|
|
|
|
- In this doctest, we see many files because we test various
|
|
- viewers::
|
|
+ sage: s = sphere()
|
|
+ sage: filename = tmp_filename(ext='.png')
|
|
+ sage: s._save_image_png(filename)
|
|
+ sage: open(filename).read().startswith('\x89PNG')
|
|
+ True
|
|
|
|
- sage: sorted(os.listdir(basedir))
|
|
- ['xyz-size500.spt', 'xyz-size500.spt.zip', 'xyz.mtl', 'xyz.obj', 'xyz.png']
|
|
+ sage: s._save_image_png('/path/to/foo.bar')
|
|
+ Traceback (most recent call last):
|
|
+ ...
|
|
+ AssertionError
|
|
"""
|
|
+ assert filename.endswith('.png')
|
|
opts = self._process_viewing_options(kwds)
|
|
-
|
|
viewer = opts['viewer']
|
|
- verbosity = opts['verbosity']
|
|
- figsize = opts['figsize']
|
|
- aspect_ratio = opts['aspect_ratio'] # this necessarily has a value now
|
|
- frame_aspect_ratio = opts['frame_aspect_ratio']
|
|
- zoom = opts['zoom']
|
|
- frame = opts['frame']
|
|
- axes = opts['axes']
|
|
-
|
|
- basename = filename # Filename without extension
|
|
- filename = None # Filename with extension of the plot result
|
|
-
|
|
- def makename(ext):
|
|
- if basename is not None:
|
|
- return basename + ext
|
|
- else:
|
|
- return graphics_filename(ext=ext)
|
|
-
|
|
- from sage.plot.plot import EMBEDDED_MODE
|
|
- from sage.doctest import DOCTEST_MODE
|
|
-
|
|
- # Tachyon resolution options
|
|
- if DOCTEST_MODE:
|
|
- tachyon_opts = '-res 10 10'
|
|
+ if viewer == 'tachyon':
|
|
+ from sage.repl.rich_output.output_catalog import OutputImagePng
|
|
+ render = self._rich_repr_tachyon(OutputImagePng, **kwds)
|
|
+ render.png.save_as(filename)
|
|
+ elif viewer == 'jmol':
|
|
+ scene = self._rich_repr_jmol(**kwds)
|
|
+ scene.preview_png.save_as(filename)
|
|
else:
|
|
- tachyon_opts = '-res %s %s'%(figsize[0]*100, figsize[1]*100)
|
|
-
|
|
- if DOCTEST_MODE or viewer=='tachyon' or (viewer=='java3d' and EMBEDDED_MODE):
|
|
- T = self._prepare_for_tachyon(frame, axes, frame_aspect_ratio, aspect_ratio, zoom)
|
|
- filename = makename(".png")
|
|
- tachyon_rt(T.tachyon(), filename, verbosity, True, tachyon_opts)
|
|
- import sage.misc.viewer
|
|
- viewer_app = sage.misc.viewer.png_viewer()
|
|
-
|
|
- if DOCTEST_MODE or viewer=='java3d':
|
|
- mtl = makename(".mtl")
|
|
- filename = makename(".obj")
|
|
- with open(filename, "w") as f:
|
|
- f.write("mtllib %s\n" % mtl)
|
|
- f.write(self.obj())
|
|
- with open(mtl, "w") as f:
|
|
- f.write(self.mtl_str())
|
|
- viewer_app = os.path.join(SAGE_LOCAL, "bin", "sage3d")
|
|
-
|
|
- if DOCTEST_MODE or viewer=='jmol':
|
|
- # Temporary hack: encode the desired applet size in the end of the filename:
|
|
- # (This will be removed once we have dynamic resizing of applets in the browser.)
|
|
- fg = figsize[0]
|
|
- sizedname = lambda ext: makename("-size{}{}".format(fg*100, ext))
|
|
-
|
|
- if EMBEDDED_MODE:
|
|
- # jmol doesn't seem to correctly parse the ?params part of a URL
|
|
- archive_name = sizedname(
|
|
- "-{}.jmol.zip".format(randint(0, 1 << 30)))
|
|
- filename = sizedname(".jmol")
|
|
- else:
|
|
- archive_name = sizedname(".spt.zip".format(fg))
|
|
- filename = sizedname(".spt")
|
|
- with open(filename, 'w') as f:
|
|
- f.write('set defaultdirectory "{0}"\n'.format(archive_name))
|
|
- f.write('script SCRIPT\n')
|
|
-
|
|
- T = self._prepare_for_jmol(frame, axes, frame_aspect_ratio, aspect_ratio, zoom)
|
|
- T.export_jmol(archive_name, force_reload=EMBEDDED_MODE, zoom=zoom*100, **kwds)
|
|
- viewer_app = os.path.join(SAGE_LOCAL, "bin", "jmol")
|
|
-
|
|
- # If the server has a Java installation we can make better static images with Jmol
|
|
- # Test for Java then make image with Jmol or Tachyon if no JavaVM
|
|
- if EMBEDDED_MODE:
|
|
- # We need a script for the Notebook.
|
|
- # When the notebook sees this file, it will know to
|
|
- # display the static file and the "Make Interactive"
|
|
- # button.
|
|
- import sagenb
|
|
- path = "cells/%s/%s" %(sagenb.notebook.interact.SAGE_CELL_ID, archive_name)
|
|
- with open(filename, 'w') as f:
|
|
- f.write('set defaultdirectory "%s"\n' % path)
|
|
- f.write('script SCRIPT\n')
|
|
-
|
|
- # Filename for the static image
|
|
- png_path = '.jmol_images'
|
|
- sage_makedirs(png_path)
|
|
- png_name = os.path.join(png_path, filename + ".png")
|
|
-
|
|
- from sage.interfaces.jmoldata import JmolData
|
|
- jdata = JmolData()
|
|
- if jdata.is_jvm_available():
|
|
- # Java needs absolute paths
|
|
- archive_name = os.path.abspath(archive_name)
|
|
- png_name = os.path.abspath(png_name)
|
|
- script = '''set defaultdirectory "%s"\nscript SCRIPT\n''' % archive_name
|
|
- jdata.export_image(targetfile=png_name, datafile=script, image_type="PNG", figsize=fg)
|
|
- else:
|
|
- # Render the image with tachyon
|
|
- T = self._prepare_for_tachyon(frame, axes, frame_aspect_ratio, aspect_ratio, zoom)
|
|
- tachyon_rt(T.tachyon(), png_name, verbosity, True, tachyon_opts)
|
|
-
|
|
- if viewer == 'canvas3d':
|
|
- if not EMBEDDED_MODE and not DOCTEST_MODE:
|
|
- raise RuntimeError("canvas3d viewer is only available from the Notebook")
|
|
- T = self._prepare_for_tachyon(frame, axes, frame_aspect_ratio, aspect_ratio, zoom)
|
|
- data = flatten_list(T.json_repr(T.default_render_params()))
|
|
- filename = makename('.canvas3d')
|
|
- with open(filename, 'w') as f:
|
|
- f.write('[%s]' % ','.join(data))
|
|
-
|
|
- if filename is None:
|
|
- raise ValueError("Unknown 3d plot type: %s" % viewer)
|
|
-
|
|
- if not DOCTEST_MODE and not EMBEDDED_MODE:
|
|
- if verbosity:
|
|
- pipes = "2>&1"
|
|
- else:
|
|
- pipes = "2>/dev/null 1>/dev/null &"
|
|
- os.system('%s "%s" %s' % (viewer_app, filename, pipes))
|
|
+ raise ValueError('cannot use viewer={0} to render image'.format(viewer))
|
|
|
|
- def save_image(self, filename=None, *args, **kwds):
|
|
+ def save_image(self, filename, **kwds):
|
|
r"""
|
|
- Save an image representation of self. The image type is
|
|
- determined by the extension of the filename. For example,
|
|
- this could be ``.png``, ``.jpg``, ``.gif``, ``.pdf``,
|
|
- ``.svg``. Currently this is implemented by calling the
|
|
- :meth:`save` method of self, passing along all arguments and
|
|
- keywords.
|
|
+ Save a 2-D image rendering.
|
|
|
|
- .. Note::
|
|
+ The image type is determined by the extension of the filename.
|
|
+ For example, this could be ``.png``, ``.jpg``, ``.gif``,
|
|
+ ``.pdf``, ``.svg``.
|
|
+
|
|
+ INPUT:
|
|
|
|
- Not all image types are necessarily implemented for all
|
|
- graphics types. See :meth:`save` for more details.
|
|
+ - ``filename`` -- string. The file name under which to save
|
|
+ the image.
|
|
+
|
|
+ Any further keyword arguments are passed to the renderer.
|
|
|
|
EXAMPLES::
|
|
|
|
- sage: f = tmp_filename() + '.png'
|
|
sage: G = sphere()
|
|
- sage: G.save_image(f)
|
|
+ sage: png = tmp_filename(ext='.png')
|
|
+ sage: G.save_image(png)
|
|
+ sage: assert open(png).read().startswith('\x89PNG')
|
|
+
|
|
+ sage: gif = tmp_filename(ext='.gif')
|
|
+ sage: G.save_image(gif)
|
|
+ sage: assert open(gif).read().startswith('GIF')
|
|
"""
|
|
- self.save(filename, *args, **kwds)
|
|
+ ext = os.path.splitext(filename)[1].lower()
|
|
+ if ext not in ['.bmp', '.png', '.gif', '.ppm', '.tiff', '.tif', '.jpg', '.jpeg']:
|
|
+ raise ValueError('unknown image file type: {0}'.format(ext))
|
|
+ if ext == '.png':
|
|
+ self._save_image_png(filename, **kwds)
|
|
+ else:
|
|
+ png = tmp_filename(ext='.png')
|
|
+ self._save_image_png(png, **kwds)
|
|
+ import PIL.Image as Image
|
|
+ Image.open(png).save(filename)
|
|
|
|
def save(self, filename, **kwds):
|
|
"""
|
|
@@ -1341,7 +1443,7 @@ end_scene""" % (render_params.antialiasing,
|
|
|
|
INPUT:
|
|
|
|
- - ``filename`` -- Specify where to save the image or object.
|
|
+ - ``filename`` -- string. Where to save the image or object.
|
|
|
|
- ``**kwds`` -- When specifying an image file to be rendered by Tachyon,
|
|
any of the viewing options accepted by show() are valid as keyword
|
|
@@ -1361,7 +1463,7 @@ end_scene""" % (render_params.antialiasing,
|
|
|
|
sage: G.save(f, zoom=2, figsize=[5, 10])
|
|
|
|
- But some extra parameters don't make since (like ``viewer``, since
|
|
+ But some extra parameters don't make sense (like ``viewer``, since
|
|
rendering is done using Tachyon only). They will be ignored::
|
|
|
|
sage: G.save(f, viewer='jmol') # Looks the same
|
|
@@ -1375,39 +1477,14 @@ end_scene""" % (render_params.antialiasing,
|
|
if ext == '' or ext == '.sobj':
|
|
SageObject.save(self, filename)
|
|
elif ext in ['.bmp', '.png', '.gif', '.ppm', '.tiff', '.tif', '.jpg', '.jpeg']:
|
|
- opts = self._process_viewing_options(kwds)
|
|
- T = self._prepare_for_tachyon(
|
|
- opts['frame'], opts['axes'], opts['frame_aspect_ratio'],
|
|
- opts['aspect_ratio'], opts['zoom']
|
|
- )
|
|
-
|
|
- if ext == '.png':
|
|
- # No conversion is necessary
|
|
- out_filename = filename
|
|
- else:
|
|
- # Save to a temporary file, and then convert using PIL
|
|
- out_filename = tmp_filename(ext=ext)
|
|
- tachyon_rt(T.tachyon(), out_filename, opts['verbosity'], True,
|
|
- '-res %s %s' % (opts['figsize'][0]*100, opts['figsize'][1]*100))
|
|
- if ext != '.png':
|
|
- import PIL.Image as Image
|
|
- Image.open(out_filename).save(filename)
|
|
+ self.save_image(filename)
|
|
elif filename.endswith('.spt.zip'):
|
|
- # Jmol zip archive
|
|
- opts = self._process_viewing_options(kwds)
|
|
- zoom = opts['zoom']
|
|
- T = self._prepare_for_jmol(
|
|
- opts['frame'],
|
|
- opts['axes'],
|
|
- opts['frame_aspect_ratio'],
|
|
- opts['aspect_ratio'],
|
|
- zoom)
|
|
- T.export_jmol(filename, zoom=zoom*100, **kwds)
|
|
+ scene = self._rich_repr_jmol(**kwds)
|
|
+ scene.jmol.save(filename)
|
|
else:
|
|
raise ValueError('filetype not supported by save()')
|
|
|
|
|
|
-
|
|
# if you add any default parameters you must update some code below
|
|
SHOW_DEFAULTS = {'viewer':'jmol',
|
|
'verbosity':0,
|
|
@@ -1563,10 +1640,10 @@ class Graphics3dGroup(Graphics3d):
|
|
sage: G = sphere() + sphere((1,2,3))
|
|
sage: print G.x3d_str()
|
|
<Transform translation='0 0 0'>
|
|
- <Shape><Sphere radius='1.0'/><Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
|
|
+ <Shape><Sphere radius='1.0'/><Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1.0' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
|
|
</Transform>
|
|
<Transform translation='1 2 3'>
|
|
- <Shape><Sphere radius='1.0'/><Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
|
|
+ <Shape><Sphere radius='1.0'/><Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1.0' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
|
|
</Transform>
|
|
"""
|
|
return "\n".join([g.x3d_str() for g in self.all])
|
|
@@ -1730,7 +1807,7 @@ class TransformGroup(Graphics3dGroup):
|
|
EXAMPLES::
|
|
|
|
sage: sphere((1,2,3)).x3d_str()
|
|
- "<Transform translation='1 2 3'>\n<Shape><Sphere radius='1.0'/><Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance></Shape>\n\n</Transform>"
|
|
+ "<Transform translation='1 2 3'>\n<Shape><Sphere radius='1.0'/><Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1.0' specularColor='0.0 0.0 0.0'/></Appearance></Shape>\n\n</Transform>"
|
|
"""
|
|
s = "<Transform"
|
|
if self._rot is not None:
|
|
@@ -1965,7 +2042,7 @@ cdef class PrimitiveObject(Graphics3d):
|
|
EXAMPLES::
|
|
|
|
sage: sphere().flatten().x3d_str()
|
|
- "<Transform>\n<Shape><Sphere radius='1.0'/><Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance></Shape>\n\n</Transform>"
|
|
+ "<Transform>\n<Shape><Sphere radius='1.0'/><Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1.0' specularColor='0.0 0.0 0.0'/></Appearance></Shape>\n\n</Transform>"
|
|
"""
|
|
return "<Shape>" + self.x3d_geometry() + self.texture.x3d_str() + "</Shape>\n"
|
|
|
|
diff --git a/src/sage/plot/plot3d/shapes.pyx b/src/sage/plot/plot3d/shapes.pyx
|
|
index 152d8fe..6ec5cb0 100644
|
|
--- a/src/sage/plot/plot3d/shapes.pyx
|
|
+++ b/src/sage/plot/plot3d/shapes.pyx
|
|
@@ -182,7 +182,7 @@ def ColorCube(size, colors, opacity=1, **kwds):
|
|
sage: c = ColorCube([1,2,3], ['red', 'blue', 'green', 'black', 'white', 'orange'], opacity=0.5)
|
|
sage: c.show()
|
|
sage: list(c.texture_set())[0].opacity
|
|
- 0.500000000000000
|
|
+ 0.5
|
|
|
|
If you omit the last 3 colors then the first three are repeated (with
|
|
repeated colors on opposing faces)::
|
|
diff --git a/src/sage/plot/plot3d/tachyon.py b/src/sage/plot/plot3d/tachyon.py
|
|
index 079c748..a8ef1aa 100644
|
|
--- a/src/sage/plot/plot3d/tachyon.py
|
|
+++ b/src/sage/plot/plot3d/tachyon.py
|
|
@@ -144,7 +144,7 @@ from sage.structure.sage_object import SageObject
|
|
from sage.misc.misc import SAGE_TMP
|
|
from sage.misc.misc import get_verbose
|
|
from sage.misc.viewer import png_viewer
|
|
-from sage.misc.temporary_file import tmp_filename, graphics_filename
|
|
+from sage.misc.temporary_file import tmp_filename
|
|
|
|
#from sage.ext import fast_tachyon_routines
|
|
|
|
@@ -418,14 +418,16 @@ class Tachyon(SageObject):
|
|
....: return q
|
|
|
|
sage: a = animate([tw_cubic(t) for t in srange(-1,1,.3)])
|
|
- sage: a
|
|
+ sage: a # optional -- ImageMagick
|
|
Animation with 7 frames
|
|
sage: a.show() # optional -- ImageMagick
|
|
"""
|
|
self.save(filename, *args, **kwds)
|
|
|
|
- def save(self, filename='sage.png', verbose=None, block=True, extra_opts=''):
|
|
+ def save(self, filename='sage.png', verbose=None, extra_opts=''):
|
|
r"""
|
|
+ Save rendering of the tachyon scene
|
|
+
|
|
INPUT:
|
|
|
|
- ``filename`` - (default: 'sage.png') output
|
|
@@ -452,9 +454,6 @@ class Tachyon(SageObject):
|
|
|
|
- ``2`` - very verbose output
|
|
|
|
- - ``block`` - bool (default: True); if False, run the
|
|
- rendering command in the background.
|
|
-
|
|
- ``extra_opts`` - passed directly to tachyon command
|
|
line. Use tachyon_rt.usage() to see some of the possibilities.
|
|
|
|
@@ -466,18 +465,52 @@ class Tachyon(SageObject):
|
|
sage: q.sphere((0,0,0),1,'s')
|
|
sage: tempname = tmp_filename()
|
|
sage: q.save(tempname)
|
|
- sage: os.system('rm ' + tempname)
|
|
- 0
|
|
"""
|
|
if verbose is None:
|
|
verbose = get_verbose()
|
|
+ tachyon_rt(self.str(), filename, verbose, extra_opts)
|
|
+
|
|
+ def _rich_repr_(self, display_manager, **kwds):
|
|
+ """
|
|
+ Rich Output Magic Method
|
|
+
|
|
+ See :mod:`sage.repl.rich_output` for details.
|
|
|
|
- tachyon_rt(self.str(), filename, verbose, block, extra_opts)
|
|
+ EXAMPLES::
|
|
|
|
- def show(self, verbose=None, extra_opts=''):
|
|
+ sage: q = Tachyon()
|
|
+ sage: q.light((1,1,11), 1,(1,1,1))
|
|
+ sage: q.texture('s')
|
|
+ sage: q.sphere((0,0,0),1,'s')
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: q._rich_repr_(dm)
|
|
+ OutputImagePng container
|
|
+ """
|
|
+ OutputImagePng = display_manager.types.OutputImagePng
|
|
+ if OutputImagePng not in display_manager.supported_output():
|
|
+ return
|
|
+ filename = tmp_filename(ext='.png')
|
|
+ self.save(filename, **kwds)
|
|
+ from sage.repl.rich_output.buffer import OutputBuffer
|
|
+ buf = OutputBuffer.from_file(filename)
|
|
+ return OutputImagePng(buf)
|
|
+
|
|
+ def show(self, **kwds):
|
|
r"""
|
|
Create a PNG file of the scene.
|
|
|
|
+ This method attempts to display the graphics immediately,
|
|
+ without waiting for the currently running code (if any) to
|
|
+ return to the command line. Be careful, calling it from within
|
|
+ a loop will potentially launch a large number of external
|
|
+ viewer programs.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ This method does not return anything. Use :meth:`save` if you
|
|
+ want to save the figure as an image.
|
|
+
|
|
EXAMPLES:
|
|
|
|
This example demonstrates how the global Sage verbosity setting
|
|
@@ -535,13 +568,9 @@ class Tachyon(SageObject):
|
|
Scene contains 1 non-gridded objects
|
|
...
|
|
"""
|
|
- filename = graphics_filename()
|
|
- self.save(filename, verbose=verbose, extra_opts=extra_opts)
|
|
-
|
|
- from sage.doctest import DOCTEST_MODE
|
|
- from sage.plot.plot import EMBEDDED_MODE
|
|
- if not DOCTEST_MODE and not EMBEDDED_MODE:
|
|
- os.system('%s %s 2>/dev/null 1>/dev/null &'%(png_viewer(), filename))
|
|
+ from sage.repl.rich_output import get_display_manager
|
|
+ dm = get_display_manager()
|
|
+ dm.display_immediately(self, **kwds)
|
|
|
|
def _res(self):
|
|
r"""
|
|
@@ -611,8 +640,8 @@ class Tachyon(SageObject):
|
|
sage: t.sphere((0,0.5,0), 0.2, 't2')
|
|
sage: t.sphere((0.5,0,0), 0.2, 't3')
|
|
sage: t.sphere((0,0,0.5), 0.2, 't4')
|
|
- sage: t.str().find('PLASTIC')
|
|
- 595
|
|
+ sage: 'PLASTIC' in t.str()
|
|
+ True
|
|
"""
|
|
return r"""
|
|
begin_scene
|
|
@@ -738,7 +767,7 @@ class Tachyon(SageObject):
|
|
sage: t.texture('s')
|
|
sage: q = t.texture_recolor('s',[(0,0,1)])
|
|
sage: t._objects[1]._color
|
|
- (0, 0, 1)
|
|
+ (0.0, 0.0, 1.0)
|
|
"""
|
|
base_tex = None
|
|
names = []
|
|
@@ -782,7 +811,7 @@ class Tachyon(SageObject):
|
|
sage: t = Tachyon()
|
|
sage: t.ring([0,0,0], [0,0,1], 1.0, 2.0, 's')
|
|
sage: t._objects[0]._center
|
|
- [0, 0, 0]
|
|
+ (0.0, 0.0, 0.0)
|
|
"""
|
|
self._objects.append(Ring(center, normal, inner, outer, texture))
|
|
|
|
@@ -991,32 +1020,25 @@ class Tachyon(SageObject):
|
|
Scene contains 514 objects.
|
|
...
|
|
"""
|
|
+ self._objects.append(
|
|
+ ParametricPlot(
|
|
+ f, t_0, t_f, tex, r=r, cylinders=cylinders,
|
|
+ min_depth=min_depth, max_depth=max_depth,
|
|
+ e_rel=.01,e_abs=.01
|
|
+ )
|
|
+ )
|
|
+
|
|
|
|
- self._objects.append(ParametricPlot(f, t_0, t_f, tex, r=r, cylinders=cylinders,min_depth=min_depth,max_depth=max_depth,e_rel=.01,e_abs=.01))
|
|
-
|
|
-#Doesn't seem to be used:
|
|
-# def collect(self, objects):
|
|
-# """
|
|
-# Add a set of objects to the scene from a collection.
|
|
-#
|
|
-# EXAMPLES::
|
|
-#
|
|
-# sage: t = Tachyon()
|
|
-# sage: t.texture('s')
|
|
-# sage: for i in range(10): t.sphere((0,0,i),i,'s')
|
|
-# """
|
|
-# self._objects.extend(objects)
|
|
-
|
|
-class Light:
|
|
+class Light(object):
|
|
r"""
|
|
Represents lighting objects.
|
|
|
|
EXAMPLES::
|
|
|
|
sage: from sage.plot.plot3d.tachyon import Light
|
|
- sage: q = Light((1,1,1),1,(1,1,1))
|
|
+ sage: q = Light((1,1,1), 1, (1,1,1))
|
|
sage: q._center
|
|
- (1, 1, 1)
|
|
+ (1.0, 1.0, 1.0)
|
|
"""
|
|
def __init__(self, center, radius, color):
|
|
r"""
|
|
@@ -1025,13 +1047,15 @@ class Light:
|
|
EXAMPLES::
|
|
|
|
sage: from sage.plot.plot3d.tachyon import Light
|
|
- sage: q = Light((1,1,1),1,(1,1,1))
|
|
+ sage: q = Light((1,1,1), 1, (1,1,1))
|
|
sage: q._color
|
|
- (1, 1, 1)
|
|
+ (1.0, 1.0, 1.0)
|
|
"""
|
|
- self._center = center
|
|
- self._radius = radius
|
|
- self._color = color
|
|
+ x, y, z = center
|
|
+ self._center = (float(x), float(y), float(z))
|
|
+ self._radius = float(radius)
|
|
+ r, g, b = color
|
|
+ self._color = (float(r), float(g), float(b))
|
|
|
|
def str(self):
|
|
r"""
|
|
@@ -1040,19 +1064,21 @@ class Light:
|
|
EXAMPLES::
|
|
|
|
sage: from sage.plot.plot3d.tachyon import Light
|
|
- sage: q = Light((1,1,1),1,(1,1,1))
|
|
+ sage: q = Light((1,1,1), 1, (1,1,1))
|
|
sage: q._radius
|
|
- 1
|
|
+ 1.0
|
|
"""
|
|
return r"""
|
|
light center %s
|
|
rad %s
|
|
color %s
|
|
- """%(tostr(self._center), float(self._radius),
|
|
+ """%(tostr(self._center), self._radius,
|
|
tostr(self._color))
|
|
|
|
-class Texfunc:
|
|
- def __init__(self, ttype=0,center=(0,0,0), rotate=(0,0,0), scale=(1,1,1), imagefile=''):
|
|
+
|
|
+class Texfunc(object):
|
|
+
|
|
+ def __init__(self, ttype=0, center=(0,0,0), rotate=(0,0,0), scale=(1,1,1), imagefile=''):
|
|
r"""
|
|
Creates a texture function.
|
|
|
|
@@ -1064,9 +1090,12 @@ class Texfunc:
|
|
0
|
|
"""
|
|
self._ttype = ttype
|
|
- self._center = center
|
|
- self._rotate = rotate
|
|
- self._scale = scale
|
|
+ x, y, z = center
|
|
+ self._center = (float(x), float(y), float(z))
|
|
+ x, y, z = rotate
|
|
+ self._rotate = (float(x), float(y), float(z))
|
|
+ x, y, z = scale
|
|
+ self._scale = (float(x), float(y), float(z))
|
|
self._imagefile = imagefile
|
|
|
|
def str(self):
|
|
@@ -1083,25 +1112,33 @@ class Texfunc:
|
|
if self._ttype == 0:
|
|
return "0"
|
|
elif self._ttype < 7 and self._ttype > 0:
|
|
- return r"""%d center %s rotate %s scale %s"""%(self._ttype,
|
|
- tostr(self._center),
|
|
- tostr(self._rotate),
|
|
- tostr(self._scale))
|
|
+ return r"""%d center %s rotate %s scale %s"""%(
|
|
+ self._ttype,
|
|
+ tostr(self._center),
|
|
+ tostr(self._rotate),
|
|
+ tostr(self._scale))
|
|
elif self._ttype < 9:
|
|
- return r"""%d %s center %s rotate %s scale %s"""%(self._ttype, self._imagefile,tostr(self._center),tostr(self._rotate),tostr(self._scale))
|
|
+ return r"""%d %s center %s rotate %s scale %s"""%(
|
|
+ self._ttype,
|
|
+ self._imagefile,
|
|
+ tostr(self._center),
|
|
+ tostr(self._rotate),
|
|
+ tostr(self._scale))
|
|
elif self._ttype == 9:
|
|
return r"""%d %s center %s rotate %s scale %s
|
|
uaxis 1.0 0.0 0.0
|
|
- vaxis 0.0 1.0 0.0"""%(self._ttype,
|
|
- self._imagefile,
|
|
- tostr(self._center),
|
|
- tostr(self._rotate),
|
|
- tostr(self._scale))
|
|
+ vaxis 0.0 1.0 0.0"""%(
|
|
+ self._ttype,
|
|
+ self._imagefile,
|
|
+ tostr(self._center),
|
|
+ tostr(self._rotate),
|
|
+ tostr(self._scale))
|
|
else:
|
|
raise ValueError
|
|
|
|
|
|
-class Texture:
|
|
+class Texture(object):
|
|
+
|
|
def __init__(self, name, ambient=0.2, diffuse=0.8,
|
|
specular=0.0, opacity=1.0,
|
|
color=(1.0,0.0, 0.5), texfunc=0,
|
|
@@ -1116,17 +1153,18 @@ class Texture:
|
|
sage: t.str().split()[2:6]
|
|
['ambient', '0.2', 'diffuse', '0.8']
|
|
"""
|
|
- self._name = name
|
|
- self._ambient = ambient
|
|
- self._diffuse = diffuse
|
|
- self._specular = specular
|
|
- self._opacity = opacity
|
|
- self._color = color
|
|
+ self._name = str(name)
|
|
+ self._ambient = float(ambient)
|
|
+ self._diffuse = float(diffuse)
|
|
+ self._specular = float(specular)
|
|
+ self._opacity = float(opacity)
|
|
+ r, g, b = color
|
|
+ self._color = (float(r), float(g), float(b))
|
|
self._texfunc = texfunc
|
|
- self._phong = phong
|
|
- self._phongsize = phongsize
|
|
- self._phongtype = phongtype
|
|
- self._imagefile = imagefile
|
|
+ self._phong = float(phong)
|
|
+ self._phongsize = float(phongsize)
|
|
+ self._phongtype = str(phongtype)
|
|
+ self._imagefile = str(imagefile)
|
|
|
|
def recolor(self, name, color):
|
|
r"""
|
|
@@ -1155,7 +1193,6 @@ class Texture:
|
|
sage: t = Texture('w')
|
|
sage: t.str().split()[2:6]
|
|
['ambient', '0.2', 'diffuse', '0.8']
|
|
-
|
|
"""
|
|
return r"""
|
|
texdef %s ambient %s diffuse %s specular %s opacity %s
|
|
@@ -1172,7 +1209,8 @@ class Texture:
|
|
tostr(self._color),
|
|
self._texfunc)
|
|
|
|
-class Sphere:
|
|
+
|
|
+class Sphere(object):
|
|
r"""
|
|
A class for creating spheres in tachyon.
|
|
"""
|
|
@@ -1184,13 +1222,14 @@ class Sphere:
|
|
|
|
sage: t = Tachyon()
|
|
sage: from sage.plot.plot3d.tachyon import Sphere
|
|
- sage: t.texture('r', color=(.8,0,0), ambient = .1)
|
|
- sage: s = Sphere((1,1,1),1,'r')
|
|
+ sage: t.texture('r', color=(.8,0,0), ambient=.1)
|
|
+ sage: s = Sphere((1,1,1), 1, 'r')
|
|
sage: s._radius
|
|
- 1
|
|
+ 1.0
|
|
"""
|
|
- self._center = center
|
|
- self._radius = radius
|
|
+ x, y, z = center
|
|
+ self._center = (float(x), float(y), float(z))
|
|
+ self._radius = float(radius)
|
|
self._texture = texture
|
|
|
|
def str(self):
|
|
@@ -1202,15 +1241,16 @@ class Sphere:
|
|
sage: t = Tachyon()
|
|
sage: from sage.plot.plot3d.tachyon import Sphere
|
|
sage: t.texture('r', color=(.8,0,0), ambient = .1)
|
|
- sage: s = Sphere((1,1,1),1,'r')
|
|
+ sage: s = Sphere((1,1,1), 1, 'r')
|
|
sage: s.str()
|
|
'\n sphere center 1.0 1.0 1.0 rad 1.0 r\n '
|
|
"""
|
|
return r"""
|
|
sphere center %s rad %s %s
|
|
- """%(tostr(self._center), float(self._radius), self._texture)
|
|
+ """%(tostr(self._center), self._radius, self._texture)
|
|
|
|
-class Ring:
|
|
+
|
|
+class Ring(object):
|
|
r"""
|
|
An annulus of zero thickness.
|
|
"""
|
|
@@ -1224,12 +1264,14 @@ class Ring:
|
|
sage: from sage.plot.plot3d.tachyon import Ring
|
|
sage: r = Ring((1,1,1), (1,1,0), 1.0, 2.0, 's')
|
|
sage: r._center
|
|
- (1, 1, 1)
|
|
+ (1.0, 1.0, 1.0)
|
|
"""
|
|
- self._center = center
|
|
- self._normal = normal
|
|
- self._inner = inner
|
|
- self._outer = outer
|
|
+ x, y, z = center
|
|
+ self._center = (float(x), float(y), float(z))
|
|
+ x, y, z = normal
|
|
+ self._normal = (float(x), float(y), float(z))
|
|
+ self._inner = float(inner)
|
|
+ self._outer = float(outer)
|
|
self._texture = texture
|
|
|
|
def str(self):
|
|
@@ -1245,9 +1287,11 @@ class Ring:
|
|
"""
|
|
return r"""
|
|
ring center %s normal %s inner %s outer %s %s
|
|
- """%(tostr(self._center), tostr(self._normal), float(self._inner), float(self._outer), self._texture)
|
|
+ """%(tostr(self._center), tostr(self._normal),
|
|
+ self._inner, self._outer, self._texture)
|
|
|
|
-class FractalLandscape:
|
|
+
|
|
+class FractalLandscape(object):
|
|
r"""
|
|
Axis-aligned fractal landscape.
|
|
Does not seem very useful at the moment, but perhaps will be improved in the future.
|
|
@@ -1261,11 +1305,14 @@ class FractalLandscape:
|
|
sage: from sage.plot.plot3d.tachyon import FractalLandscape
|
|
sage: fl = FractalLandscape([20,20],[30,30],[1,2,3],'s')
|
|
sage: fl._center
|
|
- [1, 2, 3]
|
|
+ (1.0, 2.0, 3.0)
|
|
"""
|
|
- self._res = res
|
|
- self._scale = scale
|
|
- self._center = center
|
|
+ x, y = res
|
|
+ self._res = (int(x), int(y))
|
|
+ x, y = scale
|
|
+ self._scale = (int(x), int(y))
|
|
+ x, y, z = center
|
|
+ self._center = (float(x), float(y), float(z))
|
|
self._texture = texture
|
|
|
|
def str(self):
|
|
@@ -1283,7 +1330,8 @@ class FractalLandscape:
|
|
scape res %s scale %s center %s %s
|
|
"""%(tostr(self._res, 2, int), tostr(self._scale, 2, int), tostr(self._center), self._texture)
|
|
|
|
-class Cylinder:
|
|
+
|
|
+class Cylinder(object):
|
|
r"""
|
|
An infinite cylinder.
|
|
"""
|
|
@@ -1299,9 +1347,11 @@ class Cylinder:
|
|
sage: c.str()
|
|
'\n cylinder center 0.0 0.0 0.0 axis 1.0 1.0 1.0 rad 0.1 s\n '
|
|
"""
|
|
- self._center = center
|
|
- self._axis = axis
|
|
- self._radius = radius
|
|
+ x, y, z = center
|
|
+ self._center = (float(x), float(y), float(z))
|
|
+ x, y, z = axis
|
|
+ self._axis = (float(x), float(y), float(z))
|
|
+ self._radius = float(radius)
|
|
self._texture = texture
|
|
|
|
def str(self):
|
|
@@ -1318,9 +1368,10 @@ class Cylinder:
|
|
"""
|
|
return r"""
|
|
cylinder center %s axis %s rad %s %s
|
|
- """%(tostr(self._center), tostr(self._axis), float(self._radius), self._texture)
|
|
+ """%(tostr(self._center), tostr(self._axis), self._radius, self._texture)
|
|
+
|
|
|
|
-class Plane:
|
|
+class Plane(object):
|
|
r"""
|
|
An infinite plane.
|
|
"""
|
|
@@ -1331,12 +1382,14 @@ class Plane:
|
|
EXAMPLES::
|
|
|
|
sage: from sage.plot.plot3d.tachyon import Plane
|
|
- sage: p = Plane((1,2,3),(1,2,4),'s')
|
|
+ sage: p = Plane((1,2,3), (1,2,4), 's')
|
|
sage: p.str()
|
|
'\n plane center 1.0 2.0 3.0 normal 1.0 2.0 4.0 s\n '
|
|
"""
|
|
- self._center = center
|
|
- self._normal = normal
|
|
+ x, y, z = center
|
|
+ self._center = (float(x), float(y), float(z))
|
|
+ x, y, z = normal
|
|
+ self._normal = (float(x), float(y), float(z))
|
|
self._texture = texture
|
|
|
|
def str(self):
|
|
@@ -1354,7 +1407,8 @@ class Plane:
|
|
plane center %s normal %s %s
|
|
"""%(tostr(self._center), tostr(self._normal), self._texture)
|
|
|
|
-class FCylinder:
|
|
+
|
|
+class FCylinder(object):
|
|
r"""
|
|
A finite cylinder.
|
|
"""
|
|
@@ -1369,9 +1423,11 @@ class FCylinder:
|
|
sage: fc.str()
|
|
'\n fcylinder base 0.0 0.0 0.0 apex 1.0 1.0 1.0 rad 0.1 s\n '
|
|
"""
|
|
- self._center = base
|
|
- self._axis = apex
|
|
- self._radius = radius
|
|
+ x, y, z = base
|
|
+ self._center = (float(x), float(y), float(z))
|
|
+ x, y, z = apex
|
|
+ self._axis = (float(x), float(y), float(z))
|
|
+ self._radius = float(radius)
|
|
self._texture = texture
|
|
|
|
def str(self):
|
|
@@ -1387,9 +1443,10 @@ class FCylinder:
|
|
"""
|
|
return r"""
|
|
fcylinder base %s apex %s rad %s %s
|
|
- """%(tostr(self._center), tostr(self._axis), float(self._radius), self._texture)
|
|
+ """%(tostr(self._center), tostr(self._axis), self._radius, self._texture)
|
|
+
|
|
|
|
-class Axis_aligned_box():
|
|
+class Axis_aligned_box(object):
|
|
r"""
|
|
Box with axis-aligned edges with the given min and max coordinates.
|
|
"""
|
|
@@ -1404,8 +1461,10 @@ class Axis_aligned_box():
|
|
sage: aab.str()
|
|
'\n box min 0.0 0.0 0.0 max 1.0 1.0 1.0 s\n '
|
|
"""
|
|
- self._min_p = min_p
|
|
- self._max_p = max_p
|
|
+ x, y, z = min_p
|
|
+ self._min_p = (float(x), float(y), float(z))
|
|
+ x, y, z = max_p
|
|
+ self._max_p = (float(x), float(y), float(z))
|
|
self._texture = texture
|
|
|
|
def str(self):
|
|
@@ -1423,6 +1482,7 @@ class Axis_aligned_box():
|
|
box min %s max %s %s
|
|
"""%(tostr(self._min_p), tostr(self._max_p), self._texture)
|
|
|
|
+
|
|
class TachyonTriangle(Triangle):
|
|
r"""
|
|
Basic triangle class.
|
|
@@ -1441,7 +1501,7 @@ class TachyonTriangle(Triangle):
|
|
return r"""
|
|
TRI V0 %s V1 %s V2 %s
|
|
%s
|
|
- """%(tostr(self._a), tostr(self._b),tostr(self._c), self._color)
|
|
+ """%(tostr(self._a), tostr(self._b), tostr(self._c), self._color)
|
|
|
|
|
|
class TachyonSmoothTriangle(SmoothTriangle):
|
|
@@ -1535,254 +1595,13 @@ class TachyonTriangleFactory(TriangleFactory):
|
|
sage: t = Tachyon()
|
|
sage: t.texture('s')
|
|
sage: ttf = TachyonTriangleFactory(t, 's')
|
|
- sage: ttf.get_colors([1])
|
|
+ sage: ttf.get_colors([(1,1,1)])
|
|
['SAGETEX1_0']
|
|
"""
|
|
return self._tachyon.texture_recolor(self._texture, list)
|
|
|
|
-# following classes TachyonPlot and PlotBlock seems broken and not used anywhere, so commented out. Please write to the sage-devel google-group if you are the author of these classes to comment.
|
|
-
|
|
-#class TachyonPlot:
|
|
- #Recursively plots a function of two variables by building squares of 4 triangles, checking at
|
|
- # every stage whether or not each square should be split into four more squares. This way,
|
|
- # more planar areas get fewer triangles, and areas with higher curvature get more triangles
|
|
-
|
|
-# def str(self):
|
|
-# return "".join([o.str() for o in self._objects])
|
|
-
|
|
-# def __init__(self, tachyon, f, (min_x, max_x), (min_y, max_y), tex, g = None,
|
|
-# min_depth=4, max_depth=8, e_rel = .01, e_abs = .01, num_colors = None):
|
|
-# self._tachyon = tachyon
|
|
-# self._f = f
|
|
-# self._g = g
|
|
-# self._tex = tex
|
|
-# self._min_depth = min_depth
|
|
-# self._max_depth = max_depth
|
|
-# self._e_rel = e_rel
|
|
-# self._e_abs = e_abs
|
|
-# self._objects = []
|
|
-# self._eps = min(max_x - min_x, max_y - min_y)/(2**max_depth)
|
|
-# if self._eps == 0:
|
|
-# raise ValueError, 'Plot rectangle is really a line. Make sure min_x != #max_x and min_y != max_y.'
|
|
-# self._num_colors = num_colors
|
|
-# if g is None:
|
|
-# def fcn(x,y):
|
|
-# return [self._f(x,y)]
|
|
-# else:
|
|
-# def fcn(x,y):
|
|
-# return [self._f(x,y), self._g(x,y)]
|
|
-
|
|
-# self._fcn = fcn
|
|
-
|
|
-
|
|
-# # generate the necessary data to kick-start the recursion
|
|
-# mid_x = (min_x + max_x)/2
|
|
-# mid_y = (min_y + max_y)/2
|
|
-# sw_z = fcn(min_x,min_y)
|
|
-# nw_z = fcn(min_x,max_y)
|
|
-# se_z = fcn(max_x,min_y)
|
|
-# ne_z = fcn(max_x,max_y)
|
|
-# mid_z = fcn(mid_x,mid_y)
|
|
-
|
|
-# self._min = min(sw_z[0], nw_z[0], se_z[0], ne_z[0], mid_z[0])
|
|
-# self._max = max(sw_z[0], nw_z[0], se_z[0], ne_z[0], mid_z[0])
|
|
-
|
|
-# # jump in and start building blocks
|
|
-# outer = self.plot_block(min_x, mid_x, max_x, min_y, mid_y, max_y, sw_z, nw_z, se_z, ne_z, mid_z, 0)
|
|
-#
|
|
-# # build the boundary triangles
|
|
-# self.triangulate(outer.left, outer.left_c)
|
|
-# self.triangulate(outer.top, outer.top_c)
|
|
-# self.triangulate(outer.right, outer.right_c)
|
|
-# self.triangulate(outer.bottom, outer.bottom_c)
|
|
-
|
|
-# zrange = self._max - self._min
|
|
-# if num_colors is not None and zrange != 0:
|
|
-# colors = tachyon.texture_recolor(tex, [hue(float(i/num_colors)) for i in range(num_colors)])
|
|
-
|
|
-# for o in self._objects:
|
|
-# avg_z = (o._vertex_1[2] + o._vertex_2[2] + o._vertex_3[2])/3
|
|
-# o._texture = colors[int(num_colors * (avg_z - self._min) / zrange)]
|
|
-
|
|
-# def plot_block(self, min_x, mid_x, max_x, min_y, mid_y, max_y, sw_z, nw_z, se_z, ne_z, mid_z, depth):
|
|
-
|
|
-# if depth < self._max_depth:
|
|
-# # recursion is still an option -- step in one last level if we're within tolerance
|
|
-# # and just keep going if we're not.
|
|
-# # assumption: it's cheap to build triangles, so we might as well use all the data
|
|
-# # we calculate
|
|
-
|
|
-# # big square boundary midpoints
|
|
-# mid_w_z = self._fcn(min_x, mid_y)
|
|
-# mid_n_z = self._fcn(mid_x, max_y)
|
|
-# mid_e_z = self._fcn(max_x, mid_y)
|
|
-# mid_s_z = self._fcn(mid_x, min_y)
|
|
-
|
|
-# # midpoints locations of sub_squares
|
|
-# qtr1_x = (min_x + mid_x)/2
|
|
-# qtr1_y = (min_y + mid_y)/2
|
|
-# qtr3_x = (mid_x + max_x)/2
|
|
-# qtr3_y = (mid_y + max_y)/2
|
|
-
|
|
-# # function evaluated at these midpoints
|
|
-# mid_sw_z = self._fcn(qtr1_x,qtr1_y)
|
|
-# mid_nw_z = self._fcn(qtr1_x,qtr3_y)
|
|
-# mid_se_z = self._fcn(qtr3_x,qtr1_y)
|
|
-# mid_ne_z = self._fcn(qtr3_x,qtr3_y)
|
|
-
|
|
-# # linearization estimates of midpoints
|
|
-# est_sw_z = (mid_z[0] + sw_z[0])/2
|
|
-# est_nw_z = (mid_z[0] + nw_z[0])/2
|
|
-# est_se_z = (mid_z[0] + se_z[0])/2
|
|
-# est_ne_z = (mid_z[0] + ne_z[0])/2
|
|
-
|
|
-# self.extrema([mid_w_z[0], mid_n_z[0], mid_e_z[0], mid_s_z[0], mid_sw_z[0], mid_se_z[0], mid_nw_z[0], mid_sw_z[0]])
|
|
-
|
|
-# tol_check = [(est_sw_z, mid_sw_z[0]), (est_nw_z, mid_nw_z[0]), (est_se_z, mid_se_z[0]), (est_ne_z, mid_ne_z[0])]
|
|
-
|
|
-# if depth < self._min_depth or not self.tol_list(tol_check):
|
|
-# next_depth = depth + 1
|
|
-# else:
|
|
-# #lie about the depth to halt recursion
|
|
-# next_depth = self._max_depth
|
|
-
|
|
-# # recurse into the sub-squares
|
|
-# sw = self.plot_block(min_x, qtr1_x, mid_x, min_y, qtr1_y, mid_y, sw_z, mid_w_z, mid_s_z, mid_z, mid_sw_z, next_depth)
|
|
-# nw = self.plot_block(min_x, qtr1_x, mid_x, mid_y, qtr3_y, max_y, mid_w_z, nw_z, mid_z, mid_n_z, mid_nw_z, next_depth)
|
|
-# se = self.plot_block(mid_x, qtr3_x, max_x, min_y, qtr1_y, mid_y, mid_s_z, mid_z, se_z, mid_e_z, mid_se_z, next_depth)
|
|
-# ne = self.plot_block(mid_x, qtr3_x, max_x, mid_y, qtr3_y, max_y, mid_z, mid_n_z, mid_e_z, ne_z, mid_ne_z, next_depth)
|
|
-
|
|
-# # join the sub-squares
|
|
-# self.interface(1, sw.right, sw.right_c, se.left, se.left_c)
|
|
-# self.interface(1, nw.right, nw.right_c, ne.left, ne.left_c)
|
|
-# self.interface(0, sw.top, sw.top_c, nw.bottom, nw.bottom_c)
|
|
-# self.interface(0, se.top, se.top_c, ne.bottom, ne.bottom_c)
|
|
-
|
|
-# #get the boundary information about the subsquares
|
|
-# left = sw.left + nw.left[1:]
|
|
-# left_c = sw.left_c + nw.left_c
|
|
-# right = se.right + ne.right[1:]
|
|
-# right_c = se.right_c + ne.right_c
|
|
-# top = nw.top + ne.top[1:]
|
|
-# top_c = nw.top_c + ne.top_c
|
|
-# bottom = sw.bottom + se.bottom[1:]
|
|
-# bottom_c = sw.bottom_c + se.bottom_c
|
|
-
|
|
-# else:
|
|
-# # just build the square we're in
|
|
-# if self._g is None:
|
|
-# sw = [(min_x,min_y,sw_z[0])]
|
|
-# nw = [(min_x,max_y,nw_z[0])]
|
|
-# se = [(max_x,min_y,se_z[0])]
|
|
-# ne = [(max_x,max_y,ne_z[0])]
|
|
-# c = [[(mid_x,mid_y,mid_z[0])]]
|
|
-# else:
|
|
-# sw = [(min_x,min_y,sw_z[0]),sw_z[1]]
|
|
-# nw = [(min_x,max_y,nw_z[0]),nw_z[1]]
|
|
-# se = [(max_x,min_y,se_z[0]),se_z[1]]
|
|
-# ne = [(max_x,max_y,ne_z[0]),ne_z[1]]
|
|
-# c = [[(mid_x,mid_y,mid_z[0]),mid_z[1]]]
|
|
-
|
|
-
|
|
-# left = [sw,nw]
|
|
-# left_c = c
|
|
-# top = [nw,ne]
|
|
-# top_c = c
|
|
-# right = [se,ne]
|
|
-# right_c = c
|
|
-# bottom = [sw,se]
|
|
-# bottom_c = c
|
|
-
|
|
-# return PlotBlock(left, left_c, top, top_c, right, right_c, bottom, bottom_c)
|
|
-
|
|
-# def tol(self, (est, val)):
|
|
-# # Check relative, then absolute tolerance. If both fail, return False
|
|
-# # This is a zero-safe error checker
|
|
-
|
|
-# if abs(est - val) < self._e_rel*abs(val):
|
|
-# return True
|
|
-# if abs(est - val) < self._e_abs:
|
|
-# return True
|
|
-# return False
|
|
-
|
|
-# def tol_list(self, l):
|
|
-# # Pass in a list of pairs of numbers, (est, val) to be passed to self.tol
|
|
-# # returns False if any pair does not fall within tolerance level
|
|
-
|
|
-# for p in l:
|
|
-# if not self.tol(p):
|
|
-# return False
|
|
-# return True
|
|
-
|
|
-# def interface(self, n, p, p_c, q, q_c):
|
|
-# # Takes a pair of lists of points, and compares the (n)th coordinate, and
|
|
-# # "zips" the lists together into one. The "centers", supplied in p_c and
|
|
-# # q_c are matched up such that the lists describe triangles whose sides
|
|
-# # are "perfectly" aligned. This algorithm assumes that p and q start and
|
|
-# # end at the same point, and are sorted smallest to largest.
|
|
-
|
|
-# m = [p[0]] # a sorted union of p and q
|
|
-# mpc = [p_c[0]] # centers from p_c corresponding to m
|
|
-# mqc = [q_c[0]] # centers from q_c corresponding to m
|
|
-
|
|
-# i = 1
|
|
-# j = 1
|
|
-
|
|
-# while i < len(p_c) or j < len(q_c):
|
|
-# if abs(p[i][0][n] - q[j][0][n]) < self._eps:
|
|
-# m.append(p[i])
|
|
-# mpc.append(p_c[i])
|
|
-# mqc.append(q_c[j])
|
|
-# i += 1
|
|
-# j += 1
|
|
-# elif p[i][0][n] < q[j][0][n]:
|
|
-# m.append(p[i])
|
|
-# mpc.append(p_c[i])
|
|
-# mqc.append(mqc[-1])
|
|
-# i += 1
|
|
-# else:
|
|
-# m.append(q[j])
|
|
-# mpc.append(mpc[-1])
|
|
-# mqc.append(q_c[j])
|
|
-# j += 1
|
|
-
|
|
-# m.append(p[-1])
|
|
-
|
|
-# self.triangulate(m, mpc)
|
|
-# self.triangulate(m, mqc)
|
|
-
|
|
-
|
|
-# def triangulate(self, p, c):
|
|
-# # Pass in a list of edge points (p) and center points (c).
|
|
-# # Triangles will be rendered between consecutive edge points and the
|
|
-# # center point with the same index number as the earlier edge point.
|
|
-
|
|
-# if self._g is None:
|
|
-# for i in range(0,len(p)-1):
|
|
-# self._objects.append(Triangle(p[i][0], p[i+1][0], c[i][0], self._tex))
|
|
-# else:
|
|
-# for i in range(0,len(p)-1):
|
|
-# self._objects.append(SmoothTriangle(p[i][0], p[i+1][0], c[i][0],p[i][1], p[i+1][1], c[i][1], self._tex))
|
|
-
|
|
-
|
|
-# def extrema(self, list):
|
|
-# if self._num_colors is not None:
|
|
-# self._min = min(list+[self._min])
|
|
-# self._max = max(list+[self._max])
|
|
-
|
|
-
|
|
-#class PlotBlock:
|
|
-# def __init__(self, left, left_c, top, top_c, right, right_c, bottom, bottom_c):
|
|
-# self.left = left
|
|
-# self.left_c = left_c
|
|
-# self.top = top
|
|
-# self.top_c = top_c
|
|
-# self.right = right
|
|
-# self.right_c = right_c
|
|
-# self.bottom = bottom
|
|
-# self.bottom_c = bottom_c
|
|
-
|
|
-class ParametricPlot:
|
|
+
|
|
+class ParametricPlot(object):
|
|
r"""
|
|
Parametric plotting routines.
|
|
"""
|
|
@@ -1886,7 +1705,8 @@ class ParametricPlot:
|
|
|
|
return False
|
|
|
|
-def tostr(s, length = 3, out_type = float):
|
|
+
|
|
+def tostr(s, length=3, out_type=float):
|
|
r"""
|
|
Converts vector information to a space-separated string.
|
|
|
|
diff --git a/src/sage/plot/plot3d/texture.py b/src/sage/plot/plot3d/texture.py
|
|
index 8a1f73d..66952d2 100644
|
|
--- a/src/sage/plot/plot3d/texture.py
|
|
+++ b/src/sage/plot/plot3d/texture.py
|
|
@@ -26,7 +26,7 @@ And the Texture objects keep track of all their data::
|
|
sage: T = tetrahedron(color='red', opacity=0.5)
|
|
sage: t = T.get_texture()
|
|
sage: t.opacity
|
|
- 0.500000000000000
|
|
+ 0.5
|
|
sage: T # should be translucent
|
|
Graphics3d Object
|
|
|
|
@@ -254,13 +254,18 @@ class Texture_class(SageObject):
|
|
sage: t
|
|
Texture(texture..., 6666ff)
|
|
sage: t.opacity
|
|
- 0.600000000000000
|
|
+ 0.6
|
|
sage: t.jmol_str('obj')
|
|
'color obj translucent 0.4 [102,102,255]'
|
|
sage: t.mtl_str()
|
|
- 'newmtl texture...\nKa 0.2 0.2 0.5\nKd 0.4 0.4 1.0\nKs 0.0 0.0 0.0\nillum 1\nNs 1\nd 0.600000000000000'
|
|
+ 'newmtl texture...\nKa 0.2 0.2 0.5\nKd 0.4 0.4 1.0\nKs 0.0 0.0 0.0\nillum 1\nNs 1.0\nd 0.6'
|
|
sage: t.x3d_str()
|
|
- "<Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance>"
|
|
+ "<Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1.0' specularColor='0.0 0.0 0.0'/></Appearance>"
|
|
+
|
|
+ TESTS::
|
|
+
|
|
+ sage: Texture(opacity=1/3).opacity
|
|
+ 0.3333333333333333
|
|
"""
|
|
def __init__(self, id, color=(.4, .4, 1), opacity=1, ambient=0.5, diffuse=1, specular=0, shininess=1, name=None, **kwds):
|
|
r"""
|
|
@@ -288,8 +293,8 @@ class Texture_class(SageObject):
|
|
color = (float(color[0]), float(color[1]), float(color[2]))
|
|
|
|
self.color = color
|
|
- self.opacity = opacity
|
|
- self.shininess = shininess
|
|
+ self.opacity = float(opacity)
|
|
+ self.shininess = float(shininess)
|
|
|
|
if not isinstance(ambient, tuple):
|
|
ambient = parse_color(ambient, color)
|
|
@@ -341,7 +346,7 @@ class Texture_class(SageObject):
|
|
sage: from sage.plot.plot3d.texture import Texture
|
|
sage: t = Texture(opacity=0.6)
|
|
sage: t.tachyon_str()
|
|
- 'Texdef texture...\n Ambient 0.333333333333 Diffuse 0.666666666667 Specular 0.0 Opacity 0.600000000000000\n Color 0.4 0.4 1.0\n TexFunc 0'
|
|
+ 'Texdef texture...\n Ambient 0.333333333333 Diffuse 0.666666666667 Specular 0.0 Opacity 0.6\n Color 0.4 0.4 1.0\n TexFunc 0'
|
|
"""
|
|
total_color = float(sum(self.ambient) + sum(self.diffuse) + sum(self.specular))
|
|
if total_color == 0:
|
|
@@ -364,7 +369,7 @@ class Texture_class(SageObject):
|
|
sage: from sage.plot.plot3d.texture import Texture
|
|
sage: t = Texture(opacity=0.6)
|
|
sage: t.x3d_str()
|
|
- "<Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance>"
|
|
+ "<Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1.0' specularColor='0.0 0.0 0.0'/></Appearance>"
|
|
"""
|
|
return "<Appearance><Material diffuseColor='%s %s %s' shininess='%s' specularColor='%s %s %s'/></Appearance>" % \
|
|
(self.color[0], self.color[1], self.color[2], self.shininess, self.specular[0], self.specular[0], self.specular[0])
|
|
@@ -378,7 +383,7 @@ class Texture_class(SageObject):
|
|
sage: from sage.plot.plot3d.texture import Texture
|
|
sage: t = Texture(opacity=0.6)
|
|
sage: t.mtl_str()
|
|
- 'newmtl texture...\nKa 0.2 0.2 0.5\nKd 0.4 0.4 1.0\nKs 0.0 0.0 0.0\nillum 1\nNs 1\nd 0.600000000000000'
|
|
+ 'newmtl texture...\nKa 0.2 0.2 0.5\nKd 0.4 0.4 1.0\nKs 0.0 0.0 0.0\nillum 1\nNs 1.0\nd 0.6'
|
|
"""
|
|
return "\n".join(["newmtl %s" % self.id,
|
|
"Ka %s %s %s" % self.ambient,
|
|
diff --git a/src/sage/plot/polygon.py b/src/sage/plot/polygon.py
|
|
index 8adfbc4..728d611 100644
|
|
--- a/src/sage/plot/polygon.py
|
|
+++ b/src/sage/plot/polygon.py
|
|
@@ -167,7 +167,7 @@ class Polygon(GraphicPrimitive_xydata):
|
|
Polygon defined by 4 points
|
|
sage: q=p.plot3d()
|
|
sage: q.texture.opacity
|
|
- 0.500000000000000
|
|
+ 0.5
|
|
"""
|
|
if options is None:
|
|
options = dict(self.options())
|
|
diff --git a/src/sage/plot/primitive.py b/src/sage/plot/primitive.py
|
|
index ac1c7e7..80b740e 100644
|
|
--- a/src/sage/plot/primitive.py
|
|
+++ b/src/sage/plot/primitive.py
|
|
@@ -95,7 +95,7 @@ class GraphicPrimitive(SageObject):
|
|
sage: q.thickness
|
|
4
|
|
sage: q.texture.opacity
|
|
- 0.500000000000000
|
|
+ 0.5
|
|
"""
|
|
if options is None:
|
|
options = self.options()
|
|
diff --git a/src/sage/repl/attach.py b/src/sage/repl/attach.py
|
|
index 1ee74ed..9ebc3ef 100644
|
|
--- a/src/sage/repl/attach.py
|
|
+++ b/src/sage/repl/attach.py
|
|
@@ -566,6 +566,7 @@ def reload_attached_files_if_modified():
|
|
sage: shell.run_cell('detach({0})'.format(repr(tmp)))
|
|
sage: shell.run_cell('attached_files()')
|
|
[]
|
|
+ sage: shell.quit()
|
|
"""
|
|
for filename, mtime in modified_file_iterator():
|
|
basename = os.path.basename(filename)
|
|
diff --git a/src/sage/repl/display/formatter.py b/src/sage/repl/display/formatter.py
|
|
index 90d01c1..4e3e5ec 100644
|
|
--- a/src/sage/repl/display/formatter.py
|
|
+++ b/src/sage/repl/display/formatter.py
|
|
@@ -44,7 +44,8 @@ generally, all sage expression as an ASCII art object::
|
|
1 2 2 ]
|
|
3 3 ]
|
|
, 4 , 4 ]
|
|
- sage: shell.run_cell('%display simple')
|
|
+ sage: shell.run_cell('%display default')
|
|
+ sage: shell.quit()
|
|
|
|
This other facility uses a simple
|
|
:class:`~sage.misc.ascii_art.AsciiArt` object (see and
|
|
@@ -60,50 +61,48 @@ This other facility uses a simple
|
|
#*****************************************************************************
|
|
|
|
|
|
-from IPython.core.formatters import PlainTextFormatter, warn_format_error
|
|
+from IPython.core.formatters import DisplayFormatter, PlainTextFormatter
|
|
from IPython.utils.py3compat import str_to_unicode, unicode_to_str
|
|
|
|
-from sage.repl.display.pretty_print import (
|
|
- SagePrettyPrinter, AsciiArtPrettyPrinter, TypesetPrettyPrinter
|
|
-)
|
|
+from sage.structure.sage_object import SageObject
|
|
+from sage.repl.display.pretty_print import SagePrettyPrinter
|
|
|
|
|
|
-class SagePlainTextFormatter(PlainTextFormatter):
|
|
+
|
|
+class SageDisplayFormatter(DisplayFormatter):
|
|
|
|
def __init__(self, *args, **kwds):
|
|
- r"""
|
|
- Improved plain text formatter.
|
|
+ """
|
|
+ This is where the Sage rich objects are translated to IPython
|
|
+
|
|
+ INPUT/OUTPUT:
|
|
|
|
- In particular, it has the following two features:
|
|
-
|
|
- - correctly print lists of matrices or other objects (see
|
|
- :meth:`sage.structure.parent.Parent._repr_option`),
|
|
+ See the IPython documentation.
|
|
|
|
- - print ASCII art objects (like expressions) (see
|
|
- :meth:`sage.structure.sage_object.SageObject._ascii_art_`).
|
|
-
|
|
- EXAMPLES::
|
|
-
|
|
- sage: from sage.repl.interpreter import get_test_shell
|
|
- sage: shell = get_test_shell()
|
|
- sage: shell.display_formatter.formatters['text/plain']
|
|
- <sage.repl.display.formatter.SagePlainTextFormatter object at 0x...>
|
|
- sage: shell.run_cell('a = identity_matrix(ZZ, 2); [a,a]')
|
|
- [
|
|
- [1 0] [1 0]
|
|
- [0 1], [0 1]
|
|
- ]
|
|
+ EXAMPLES:
|
|
+
|
|
+ This is part of how Sage works with the IPython output
|
|
+ system. It cannot be used in doctests::
|
|
+
|
|
+ sage: from sage.repl.display.formatter import SageDisplayFormatter
|
|
+ sage: fmt = SageDisplayFormatter()
|
|
+ Traceback (most recent call last):
|
|
+ ...
|
|
+ RuntimeError: check failed: current backend is invalid
|
|
"""
|
|
- super(SagePlainTextFormatter, self).__init__(*args, **kwds)
|
|
- self.set_display('simple')
|
|
+ super(SageDisplayFormatter, self).__init__(*args, **kwds)
|
|
+ from sage.repl.rich_output.display_manager import get_display_manager
|
|
+ self.dm = get_display_manager()
|
|
+ from sage.repl.rich_output.backend_ipython import BackendIPython
|
|
+ self.dm.check_backend_class(BackendIPython)
|
|
|
|
- def set_display(self, mode):
|
|
- r"""
|
|
- Select the text formatting method.
|
|
+ def format(self, obj, include=None, exclude=None):
|
|
+ """
|
|
+ Use the Sage rich output instead of IPython
|
|
|
|
- INPUT:
|
|
+ INPUT/OUTPUT:
|
|
|
|
- - ``mode`` -- string. One of ``simple``, ``ascii_art``, or ``typeset``.
|
|
+ See the IPython documentation.
|
|
|
|
EXAMPLES::
|
|
|
|
@@ -123,74 +122,44 @@ class SagePlainTextFormatter(PlainTextFormatter):
|
|
sage: shell.run_cell('sum(i*x^i, i, 0, 10)')
|
|
10 9 8 7 6 5 4 3 2
|
|
10*x + 9*x + 8*x + 7*x + 6*x + 5*x + 4*x + 3*x + 2*x + x
|
|
- sage: shell.run_cell('%display simple')
|
|
- """
|
|
- if mode not in ['simple', 'ascii_art', 'typeset']:
|
|
- raise ValueError('invalid mode set')
|
|
- self._mode = mode
|
|
- self._pretty_printer_class = dict(
|
|
- simple=SagePrettyPrinter,
|
|
- ascii_art=AsciiArtPrettyPrinter,
|
|
- typeset=TypesetPrettyPrinter,
|
|
- )[mode]
|
|
-
|
|
- _mode = 'simple'
|
|
-
|
|
- @property
|
|
- def simple(self):
|
|
- """
|
|
- Whether the mode is the "simple" (default) display.
|
|
-
|
|
- OUTPUT:
|
|
-
|
|
- Boolean.
|
|
-
|
|
- EXAMPLES::
|
|
-
|
|
- sage: sys.displayhook.formatter.simple
|
|
- True
|
|
- sage: sys.displayhook.formatter.ascii_art
|
|
- False
|
|
+ sage: shell.run_cell('%display default')
|
|
+ sage: shell.quit()
|
|
"""
|
|
- return self._mode == 'simple'
|
|
+ return self.dm.displayhook(obj)
|
|
+
|
|
|
|
- @property
|
|
- def ascii_art(self):
|
|
- """
|
|
- Whether the mode is the ascii art display.
|
|
|
|
- OUTPUT:
|
|
-
|
|
- Boolean.
|
|
+class SagePlainTextFormatter(PlainTextFormatter):
|
|
|
|
- EXAMPLES::
|
|
+ def __init__(self, *args, **kwds):
|
|
+ r"""
|
|
+ Improved plain text IPython formatter.
|
|
|
|
- sage: sys.displayhook.formatter.simple
|
|
- True
|
|
- sage: sys.displayhook.formatter.ascii_art
|
|
- False
|
|
- """
|
|
- return self._mode == 'ascii_art'
|
|
+ In particular, it correctly print lists of matrices or other
|
|
+ objects (see
|
|
+ :meth:`sage.structure.parent.Parent._repr_option`).
|
|
+
|
|
+ .. warning::
|
|
|
|
- @property
|
|
- def typeset(self):
|
|
- """
|
|
- Whether the mode is the notebook "Typeset" display.
|
|
+ This IPython formatter is NOT used. You could use it to
|
|
+ enable Sage formatting in IPython, but Sage uses its own
|
|
+ rich output system that is more flexible and supports
|
|
+ different backends.
|
|
|
|
- OUTPUT:
|
|
+ INPUT/OUTPUT:
|
|
|
|
- Boolean.
|
|
+ See the IPython documentation.
|
|
|
|
EXAMPLES::
|
|
-
|
|
- sage: sys.displayhook.formatter.simple
|
|
- True
|
|
- sage: sys.displayhook.formatter.typeset
|
|
- False
|
|
+
|
|
+ sage: from sage.repl.interpreter import get_test_shell
|
|
+ sage: shell = get_test_shell()
|
|
+ sage: shell.display_formatter.formatters['text/plain']
|
|
+ <sage.repl.display.formatter.SagePlainTextFormatter object at 0x...>
|
|
+ sage: shell.quit()
|
|
"""
|
|
- return self._mode == 'typeset'
|
|
+ super(SagePlainTextFormatter, self).__init__(*args, **kwds)
|
|
|
|
- @warn_format_error
|
|
def __call__(self, obj):
|
|
"""
|
|
Compute the pretty representation of the object.
|
|
@@ -207,179 +176,27 @@ class SagePlainTextFormatter(PlainTextFormatter):
|
|
|
|
EXAMPLES::
|
|
|
|
- sage: from sage.repl.interpreter import get_test_shell
|
|
- sage: shell = get_test_shell()
|
|
- sage: fmt = shell.display_formatter.formatters['text/plain']
|
|
+ sage: from sage.repl.display.formatter import SagePlainTextFormatter
|
|
+ sage: fmt = SagePlainTextFormatter()
|
|
sage: fmt
|
|
<sage.repl.display.formatter.SagePlainTextFormatter object at 0x...>
|
|
- sage: shell.displayhook.compute_format_data(2)
|
|
- ({u'text/plain': '2'}, {})
|
|
+ sage: fmt(2)
|
|
+ ---- calling ipython formatter ----
|
|
+ '2'
|
|
sage: a = identity_matrix(ZZ, 2)
|
|
- sage: shell.displayhook.compute_format_data([a,a])
|
|
- ({u'text/plain': '[\n[1 0] [1 0]\n[0 1], [0 1]\n]'}, {})
|
|
- sage: fmt.set_display('ascii_art')
|
|
- sage: shell.displayhook.compute_format_data([a,a])
|
|
- ({u'text/plain': '[ [1 0] [1 0] ]\n[ [0 1], [0 1] ]'}, {})
|
|
- sage: i = var('i')
|
|
- sage: shell.displayhook.compute_format_data(sum(i*x^i, i, 0, 10))
|
|
- ({u'text/plain': ' 10 9 8 7 6 5 4 3
|
|
- 2 \n10*x + 9*x + 8*x + 7*x + 6*x + 5*x + 4*x + 3*x + 2*x + x'},
|
|
- {})
|
|
- sage: fmt.set_display('simple')
|
|
- """
|
|
+ sage: fmt([a, a])
|
|
+ ---- calling ipython formatter ----
|
|
+ '[\n[1 0] [1 0]\n[0 1], [0 1]\n]'
|
|
+ """
|
|
+ from sage.doctest import DOCTEST_MODE
|
|
+ if DOCTEST_MODE:
|
|
+ # Just to show that this is never executed in any other doctests in the Sage library
|
|
+ print('---- calling ipython formatter ----')
|
|
import StringIO
|
|
stream = StringIO.StringIO()
|
|
- printer = self._pretty_printer_class(
|
|
+ printer = SagePrettyPrinter(
|
|
stream, self.max_width, unicode_to_str(self.newline))
|
|
printer.pretty(obj)
|
|
printer.flush()
|
|
return stream.getvalue()
|
|
|
|
-
|
|
-class SageDoctestTextFormatter(SagePlainTextFormatter):
|
|
-
|
|
- @warn_format_error
|
|
- def __call__(self, obj):
|
|
- """
|
|
- Display ``obj``.
|
|
-
|
|
- For doctests, we both
|
|
-
|
|
- * Print the textual representation. This makes it clear in the
|
|
- documentation that the command returns graphics.
|
|
-
|
|
- * Run ``show()`` on graphics objects, to test that it
|
|
- correctly generates graphics. Note that, in
|
|
- ``DOCTEST_MODE``, the ``show()`` method will save graphics
|
|
- to temporary files but not launch a viewer.
|
|
-
|
|
- INPUT:
|
|
-
|
|
- - ``obj`` -- anything.
|
|
-
|
|
- OUTPUT:
|
|
-
|
|
- String. The plain text representation.
|
|
-
|
|
- EXAMPLES::
|
|
-
|
|
- sage: class FooGraphics(SageObject):
|
|
- ....: def _graphics_(self, **kwds):
|
|
- ....: print('showing graphics')
|
|
- ....: from sage.structure.graphics_file import GraphicsFile
|
|
- ....: return GraphicsFile('/nonexistent.png', 'image/png')
|
|
- ....: def _repr_(self):
|
|
- ....: return 'Textual representation'
|
|
- sage: from sage.repl.display.formatter import SageDoctestTextFormatter
|
|
- sage: fmt = SageDoctestTextFormatter()
|
|
- sage: fmt(FooGraphics())
|
|
- showing graphics
|
|
- 'Textual representation'
|
|
- """
|
|
- from sage.structure.sage_object import SageObject
|
|
- if isinstance(obj, SageObject) and hasattr(obj, '_graphics_'):
|
|
- obj._graphics_() # ignore whether there actually is graphics
|
|
- return super(SageDoctestTextFormatter, self).__call__(obj)
|
|
-
|
|
-
|
|
-class SageNBTextFormatter(SagePlainTextFormatter):
|
|
-
|
|
- @warn_format_error
|
|
- def __call__(self, obj):
|
|
- """
|
|
- Display ``obj``.
|
|
-
|
|
- .. warning::
|
|
-
|
|
- This is mostly a workaround for the old Notebook. Do not
|
|
- use it as a model for your own code.
|
|
-
|
|
- This is the default formatter for the old Sage Notebook
|
|
- (SageNB).
|
|
-
|
|
- INPUT:
|
|
-
|
|
- - ``obj`` -- anything.
|
|
-
|
|
- OUTPUT:
|
|
-
|
|
- String. The plain text representation. Graphics output is a
|
|
- side effect.
|
|
-
|
|
- EXAMPLES::
|
|
-
|
|
- sage: class FooGraphics(SageObject):
|
|
- ....: def _graphics_(self, **kwds):
|
|
- ....: print('showing graphics')
|
|
- ....: from sage.structure.graphics_file import GraphicsFile
|
|
- ....: from sage.misc.temporary_file import graphics_filename
|
|
- ....: return GraphicsFile(graphics_filename('.png'), 'image/png')
|
|
- ....: def _repr_(self):
|
|
- ....: return 'Textual representation'
|
|
- sage: from sage.repl.display.formatter import SageNBTextFormatter
|
|
- sage: fmt = SageNBTextFormatter()
|
|
- sage: fmt(FooGraphics())
|
|
- showing graphics
|
|
- ''
|
|
- """
|
|
- from sage.plot.plot3d.base import Graphics3d
|
|
- if isinstance(obj, Graphics3d):
|
|
- # TODO: Clean up Graphics3d and remove all the hardcoded SageNB stuff
|
|
- obj.show()
|
|
- return ''
|
|
- from sage.structure.sage_object import SageObject
|
|
- if isinstance(obj, SageObject) and hasattr(obj, '_graphics_'):
|
|
- gfx = obj._graphics_()
|
|
- if gfx:
|
|
- gfx.sagenb_embedding()
|
|
- return ''
|
|
- return super(SageNBTextFormatter, self).__call__(obj)
|
|
-
|
|
-
|
|
-class SageConsoleTextFormatter(SagePlainTextFormatter):
|
|
-
|
|
- @warn_format_error
|
|
- def __call__(self, obj):
|
|
- """
|
|
- Display ``obj``.
|
|
-
|
|
- This is the default formatter for the Sage command line
|
|
- interface.
|
|
-
|
|
- If the object is graphics, the empty string is written to the
|
|
- output. An external viewer is started in a separate process as
|
|
- a side effect.
|
|
-
|
|
- Otherwise, the usual textual representation is generated.
|
|
-
|
|
- INPUT:
|
|
-
|
|
- - ``obj`` -- anything.
|
|
-
|
|
- OUTPUT:
|
|
-
|
|
- String. The plain text representation. Third-party viewers for
|
|
- media files can be launched as a side effect.
|
|
-
|
|
- EXAMPLES::
|
|
-
|
|
- sage: class FooGraphics(SageObject):
|
|
- ....: def _graphics_(self, **kwds):
|
|
- ....: print('showing graphics')
|
|
- ....: from sage.structure.graphics_file import GraphicsFile
|
|
- ....: return GraphicsFile('/nonexistent.png', 'image/png')
|
|
- ....: def _repr_(self):
|
|
- ....: return 'Textual representation'
|
|
- sage: from sage.repl.display.formatter import SageConsoleTextFormatter
|
|
- sage: fmt = SageConsoleTextFormatter()
|
|
- sage: fmt(FooGraphics())
|
|
- showing graphics
|
|
- ''
|
|
- """
|
|
- from sage.structure.sage_object import SageObject
|
|
- if isinstance(obj, SageObject) and hasattr(obj, '_graphics_'):
|
|
- gfx = obj._graphics_()
|
|
- if gfx:
|
|
- gfx.launch_viewer()
|
|
- return ''
|
|
- return super(SageConsoleTextFormatter, self).__call__(obj)
|
|
diff --git a/src/sage/repl/display/python_hook.py b/src/sage/repl/display/python_hook.py
|
|
deleted file mode 100644
|
|
index f9d0ce8..0000000
|
|
--- a/src/sage/repl/display/python_hook.py
|
|
+++ /dev/null
|
|
@@ -1,62 +0,0 @@
|
|
-"""
|
|
-The display hook for plain Python
|
|
-
|
|
-This is not used directly in interactive Sage (where we use the
|
|
-IPython system for display hooks). This class provides a way to use
|
|
-the Sage "plain text" display formatting when not using interactive
|
|
-Sage, for example when running doctests.
|
|
-"""
|
|
-
|
|
-#*****************************************************************************
|
|
-# Copyright (C) 2014 Volker Braun <vbraun.name@gmail.com>
|
|
-#
|
|
-# Distributed under the terms of the GNU General Public License (GPL)
|
|
-# as published by the Free Software Foundation; either version 2 of
|
|
-# the License, or (at your option) any later version.
|
|
-# http://www.gnu.org/licenses/
|
|
-#*****************************************************************************
|
|
-
|
|
-
|
|
-import __builtin__
|
|
-
|
|
-
|
|
-from sage.repl.display.formatter import SageDoctestTextFormatter
|
|
-
|
|
-
|
|
-class DoctestDisplayHook(object):
|
|
-
|
|
- def __init__(self):
|
|
- """
|
|
- Python constructor
|
|
-
|
|
- EXAMPLES::
|
|
-
|
|
- sage: from sage.repl.display.python_hook import DoctestDisplayHook
|
|
- sage: d = DoctestDisplayHook()
|
|
- sage: d(set([1, 2, 3])) # Sage commandline output
|
|
- {1, 2, 3}
|
|
- sage: print(set([1, 2, 3])) # Plain Python output
|
|
- set([1, 2, 3])
|
|
- """
|
|
- self.formatter = SageDoctestTextFormatter()
|
|
-
|
|
- def __call__(self, obj):
|
|
- """
|
|
- Format the object using Sage's formatting, or format it using the old
|
|
- display hook if Sage does not want to handle the object.
|
|
-
|
|
- EXAMPLES::
|
|
-
|
|
- sage: from sage.repl.display.python_hook import DoctestDisplayHook
|
|
- sage: d = DoctestDisplayHook()
|
|
- sage: d((identity_matrix(3), identity_matrix(3)))
|
|
- (
|
|
- [1 0 0] [1 0 0]
|
|
- [0 1 0] [0 1 0]
|
|
- [0 0 1], [0 0 1]
|
|
- )
|
|
- """
|
|
- if obj is None:
|
|
- return
|
|
- print(self.formatter(obj))
|
|
- __builtin__._ = obj
|
|
diff --git a/src/sage/repl/image.py b/src/sage/repl/image.py
|
|
new file mode 100644
|
|
index 0000000..187205e
|
|
--- /dev/null
|
|
+++ b/src/sage/repl/image.py
|
|
@@ -0,0 +1,306 @@
|
|
+# -*- encoding: utf-8 -*-
|
|
+"""
|
|
+Sage Wrapper for Bitmap Images
|
|
+
|
|
+Some computations in Sage return bitmap images, for example matrices
|
|
+can be turned into bitmaps directly. Note that this is different from
|
|
+all plotting functionality, the latter can equally produce vector
|
|
+graphics. This module is about bitmaps only, and a shallow wrapper
|
|
+around ``PIL.Image``. The only difference is that :class:`Image`
|
|
+is displayed as graphics by the Sage if the UI can.
|
|
+
|
|
+EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.image import Image
|
|
+ sage: img = Image('RGB', (256, 256), 'white')
|
|
+ sage: pixels = img.pixels()
|
|
+ sage: for x, y in CartesianProduct(range(img.width()), range(img.height())):
|
|
+ ....: pixels[x, y] = (x, y, 100)
|
|
+ sage: img
|
|
+ 256x256px 24-bit RGB image
|
|
+ sage: type(img)
|
|
+ <class 'sage.repl.image.Image'>
|
|
+"""
|
|
+
|
|
+#*****************************************************************************
|
|
+# Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com>
|
|
+#
|
|
+# Distributed under the terms of the GNU General Public License (GPL)
|
|
+# as published by the Free Software Foundation; either version 2 of
|
|
+# the License, or (at your option) any later version.
|
|
+# http://www.gnu.org/licenses/
|
|
+#*****************************************************************************
|
|
+
|
|
+
|
|
+import PIL.Image
|
|
+from sage.structure.sage_object import SageObject
|
|
+
|
|
+
|
|
+class Image(SageObject):
|
|
+
|
|
+ def __init__(self, mode, size, color=0):
|
|
+ """
|
|
+ Creates a new image with the given mode and size.
|
|
+
|
|
+ INPUT::
|
|
+
|
|
+ - ``mode`` -- string. The mode to use for the new image. Valid
|
|
+ options are:
|
|
+
|
|
+ * ``'1'`` (1-bit pixels, black and white, stored with
|
|
+ one pixel per byte)
|
|
+
|
|
+ * ``'L'`` (8-bit pixels, black and white)
|
|
+
|
|
+ * ``'P'`` (8-bit pixels, mapped to any other mode using
|
|
+ a color palette)
|
|
+
|
|
+ * ``'RGB'`` (3x8-bit pixels, true color)
|
|
+
|
|
+ * ``'RGBA'`` (4x8-bit pixels, true color with
|
|
+ transparency mask)
|
|
+
|
|
+ * ``'CMYK'`` (4x8-bit pixels, color separation)
|
|
+
|
|
+ * ``'YCbCr'`` (3x8-bit pixels, color video format)
|
|
+
|
|
+ * ``'LAB'`` (3x8-bit pixels, the L*a*b color space)
|
|
+
|
|
+ * ``'HSV'`` (3x8-bit pixels, Hue, Saturation, Value
|
|
+ color space)
|
|
+
|
|
+ * ``'I'`` (32-bit signed integer pixels)
|
|
+
|
|
+ * ``'F'`` (32-bit floating point pixels)
|
|
+
|
|
+ - ``size`` -- 2-tuple, containing (width, height) in pixels.
|
|
+
|
|
+ - ``color`` -- string or numeric. What colour to use for the
|
|
+ image. Default is black. If given, this should be a single
|
|
+ integer or floating point value for single-band modes, and a
|
|
+ tuple for multi-band modes (one value per band). When
|
|
+ creating RGB images, you can also use colour strings as
|
|
+ supported by the ImageColor module. If the colour is None,
|
|
+ the image is not initialised.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ A new :class:`Image` object.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.image import Image
|
|
+ sage: Image('P', (16, 16), 13)
|
|
+ 16x16px 8-bit Color image
|
|
+ """
|
|
+ self._pil = PIL.Image.new(mode, size, color)
|
|
+
|
|
+ @property
|
|
+ def pil(self):
|
|
+ """
|
|
+ Access the wrapped PIL(low) Image
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ The underlying ``PIL.Image.Image object``.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.image import Image
|
|
+ sage: img = Image('RGB', (16, 16), 'white')
|
|
+ sage: img.pil
|
|
+ <PIL.Image.Image image mode=RGB size=16x16 at 0x...>
|
|
+ """
|
|
+ return self._pil
|
|
+
|
|
+ def pixels(self):
|
|
+ """
|
|
+ Return the pixel map
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ The PIL PixelAccess object that allows you to get/set the
|
|
+ pixel data.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.image import Image
|
|
+ sage: img = Image('RGB', (16, 16), 'white')
|
|
+ sage: img.pixels()
|
|
+ <PixelAccess object at 0x...>
|
|
+ """
|
|
+ return self._pil.load()
|
|
+
|
|
+ def _repr_(self):
|
|
+ """
|
|
+ Return string representation.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.image import Image
|
|
+ sage: Image('RGB', (16, 16), 'white') # indirect doctest
|
|
+ 16x16px 24-bit RGB image
|
|
+ """
|
|
+ modestr = {
|
|
+ '1': '{0}x{1}px BW image',
|
|
+ 'L': '{0}x{1}px 8-bit BW image',
|
|
+ 'P': '{0}x{1}px 8-bit Color image',
|
|
+ 'RGB': '{0}x{1}px 24-bit RGB image',
|
|
+ 'RGBA': '{0}x{1}px 32-bit RGBA image',
|
|
+ 'CMYK': '{0}x{1}px 24-bit CMYK image',
|
|
+ 'YCbCr': '{0}x{1}px 24-bit YCbCr mage',
|
|
+ 'LAB': '{0}x{1}px 24-bit LAB image',
|
|
+ 'HSV': '{0}x{1}px 24-bit HSV image',
|
|
+ 'I': '{0}x{1}px 32-bit signed integer image',
|
|
+ 'F': '{0}x{1}px 32-bit float image',
|
|
+ }
|
|
+ try:
|
|
+ mode = modestr[self.pil.mode]
|
|
+ except AttributeError:
|
|
+ mode = 'Unknown mode'
|
|
+ width, height = self.pil.size
|
|
+ return mode.format(width, height)
|
|
+
|
|
+ def mode(self):
|
|
+ """
|
|
+ Return the color mode
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String. As given when constructing the image.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.image import Image
|
|
+ sage: img = Image('YCbCr', (16, 16), 'white')
|
|
+ sage: img.mode()
|
|
+ 'YCbCr'
|
|
+ """
|
|
+ return self.pil.mode
|
|
+
|
|
+ def width(self):
|
|
+ """
|
|
+ Return the horizontal dimension in pixels
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Integer.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.image import Image
|
|
+ sage: img = Image('1', (12, 34), 'white')
|
|
+ sage: img.width()
|
|
+ 12
|
|
+ sage: img.height()
|
|
+ 34
|
|
+ """
|
|
+ return self.pil.size[0]
|
|
+
|
|
+ def height(self):
|
|
+ """
|
|
+ Return the vertical dimension in pixels
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Integer.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.image import Image
|
|
+ sage: img = Image('1', (12, 34), 'white')
|
|
+ sage: img.width()
|
|
+ 12
|
|
+ sage: img.height()
|
|
+ 34
|
|
+ """
|
|
+ return self.pil.size[1]
|
|
+
|
|
+ def save(self, filename):
|
|
+ """
|
|
+ Save the bitmap image
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``filename`` -- string. The filename to save as. The given
|
|
+ extension automatically determines the image file type.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.image import Image
|
|
+ sage: img = Image('P', (12, 34), 13)
|
|
+ sage: filename = tmp_filename(ext='.png')
|
|
+ sage: img.save(filename)
|
|
+ sage: open(filename).read().startswith('\x89PNG')
|
|
+ True
|
|
+ """
|
|
+ self.pil.save(filename)
|
|
+
|
|
+ def show(self):
|
|
+ r"""
|
|
+ Show this image immediately.
|
|
+
|
|
+ This method attempts to display the graphics immediately,
|
|
+ without waiting for the currently running code (if any) to
|
|
+ return to the command line. Be careful, calling it from within
|
|
+ a loop will potentially launch a large number of external
|
|
+ viewer programs.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ This method does not return anything. Use :meth:`save` if you
|
|
+ want to save the figure as an image.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.image import Image
|
|
+ sage: img = Image('1', (12, 34), 'white')
|
|
+ sage: img.show()
|
|
+ """
|
|
+ from sage.repl.rich_output import get_display_manager
|
|
+ dm = get_display_manager()
|
|
+ dm.display_immediately(self)
|
|
+
|
|
+ def _rich_repr_(self, display_manager, **kwds):
|
|
+ """
|
|
+ Rich Output Magic Method
|
|
+
|
|
+ See :mod:`sage.repl.rich_output` for details.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.image import Image
|
|
+ sage: img = Image('1', (16, 16), 'white')
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: img._rich_repr_(dm)
|
|
+ OutputImagePng container
|
|
+
|
|
+ sage: img = Image('F', (16, 16), 'white') # not supported in PNG
|
|
+ sage: img._rich_repr_(dm)
|
|
+ OutputImageGif container
|
|
+ """
|
|
+ if display_manager.preferences.graphics == 'disable':
|
|
+ return
|
|
+ types = display_manager.types
|
|
+ preferred = (
|
|
+ ('PNG', types.OutputImagePng),
|
|
+ ('JPEG', types.OutputImageJpg),
|
|
+ ('GIF', types.OutputImageGif),
|
|
+ )
|
|
+ import StringIO
|
|
+ from sage.repl.rich_output.buffer import OutputBuffer
|
|
+ for format, output_container in preferred:
|
|
+ if output_container in display_manager.supported_output():
|
|
+ stream = StringIO.StringIO()
|
|
+ try:
|
|
+ self.pil.save(stream, format=format)
|
|
+ except IOError:
|
|
+ # not all formats support all modes, e.g. no alpha support in gif
|
|
+ continue
|
|
+ buf = OutputBuffer(stream.getvalue())
|
|
+ return output_container(buf)
|
|
diff --git a/src/sage/repl/inputhook.pyx b/src/sage/repl/inputhook.pyx
|
|
index 379ba4f..4a64c67 100644
|
|
--- a/src/sage/repl/inputhook.pyx
|
|
+++ b/src/sage/repl/inputhook.pyx
|
|
@@ -103,6 +103,7 @@ def sage_inputhook():
|
|
sage: shell.run_cell('detach({0})'.format(repr(tmp)))
|
|
sage: shell.run_cell('attached_files()')
|
|
[]
|
|
+ sage: shell.quit()
|
|
"""
|
|
reload_attached_files_if_modified()
|
|
return 0
|
|
diff --git a/src/sage/repl/interpreter.py b/src/sage/repl/interpreter.py
|
|
index 0e2d692..5c0b980 100644
|
|
--- a/src/sage/repl/interpreter.py
|
|
+++ b/src/sage/repl/interpreter.py
|
|
@@ -73,7 +73,8 @@ TESTS:
|
|
Check that Cython source code appears in tracebacks::
|
|
|
|
sage: from sage.repl.interpreter import get_test_shell
|
|
- sage: get_test_shell().run_cell('1/0')
|
|
+ sage: shell = get_test_shell()
|
|
+ sage: shell.run_cell('1/0')
|
|
---------------------------------------------------------------------------
|
|
.../sage/rings/integer_ring.pyx in sage.rings.integer_ring.IntegerRing_class._div (build/cythonized/sage/rings/integer_ring.c:...)()
|
|
... cdef rational.Rational x = rational.Rational.__new__(rational.Rational)
|
|
@@ -83,6 +84,7 @@ Check that Cython source code appears in tracebacks::
|
|
... mpz_set(mpq_denref(x.value), right.value)
|
|
<BLANKLINE>
|
|
ZeroDivisionError: Rational division by zero
|
|
+ sage: shell.quit()
|
|
"""
|
|
|
|
#*****************************************************************************
|
|
@@ -103,28 +105,12 @@ import sys
|
|
from sage.repl.preparse import preparse
|
|
|
|
from IPython import Config
|
|
+from IPython.utils.traitlets import Bool, Type
|
|
|
|
from sage.env import SAGE_LOCAL
|
|
|
|
SAGE_EXTENSION = 'sage'
|
|
|
|
-DEFAULT_SAGE_CONFIG = Config(
|
|
- PromptManager = Config(
|
|
- in_template = 'sage: ',
|
|
- in2_template = '....: ',
|
|
- justify = False,
|
|
- out_template = ''),
|
|
- TerminalIPythonApp = Config(
|
|
- display_banner = False,
|
|
- verbose_crash = True),
|
|
- InteractiveShell = Config(
|
|
- ast_node_interactivity = 'all',
|
|
- colors = 'LightBG' if sys.stdout.isatty() else 'NoColor',
|
|
- confirm_exit = False,
|
|
- separate_in = ''),
|
|
- InteractiveShellApp = Config(extensions=[SAGE_EXTENSION]),
|
|
- )
|
|
-
|
|
def embedded():
|
|
"""
|
|
Returns True if Sage is being run from the notebook.
|
|
@@ -185,6 +171,7 @@ class SageShellOverride(object):
|
|
sage: shell = get_test_shell()
|
|
sage: shell.run_cell('?')
|
|
Welcome to Sage ...
|
|
+ sage: shell.quit()
|
|
"""
|
|
from sage.misc.sagedoc import help
|
|
help()
|
|
@@ -216,6 +203,7 @@ class SageShellOverride(object):
|
|
R version ...
|
|
sage: shell.user_ns['_exit_code']
|
|
0
|
|
+ sage: shell.quit()
|
|
"""
|
|
path = os.path.join(SAGE_LOCAL, 'bin',
|
|
re.split(r'[\s|;&]', cmd)[0])
|
|
@@ -230,11 +218,191 @@ class SageShellOverride(object):
|
|
from IPython.core.interactiveshell import InteractiveShell
|
|
from IPython.terminal.interactiveshell import TerminalInteractiveShell
|
|
|
|
-class SageInteractiveShell(SageShellOverride, InteractiveShell):
|
|
- pass
|
|
+
|
|
+class SageNotebookInteractiveShell(SageShellOverride, InteractiveShell):
|
|
+ """
|
|
+ IPython Shell for the Sage IPython Notebook
|
|
+
|
|
+ The doctests are not tested since they would change the current
|
|
+ rich output backend away from the doctest rich output backend.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.interpreter import SageNotebookInteractiveShell
|
|
+ sage: SageNotebookInteractiveShell() # not tested
|
|
+ <sage.repl.interpreter.SageNotebookInteractiveShell object at 0x...>
|
|
+ """
|
|
+
|
|
+ def init_display_formatter(self):
|
|
+ """
|
|
+ Switch to the Sage IPython notebook rich output backend
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.interpreter import SageNotebookInteractiveShell
|
|
+ sage: SageNotebookInteractiveShell().init_display_formatter() # not tested
|
|
+ """
|
|
+ from sage.repl.rich_output.backend_ipython import BackendIPythonNotebook
|
|
+ backend = BackendIPythonNotebook()
|
|
+ backend.get_display_manager().switch_backend(backend, shell=self)
|
|
+
|
|
|
|
class SageTerminalInteractiveShell(SageShellOverride, TerminalInteractiveShell):
|
|
- pass
|
|
+ """
|
|
+ IPython Shell for the Sage IPython Commandline Interface
|
|
+
|
|
+ The doctests are not tested since they would change the current
|
|
+ rich output backend away from the doctest rich output backend.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.interpreter import SageTerminalInteractiveShell
|
|
+ sage: SageTerminalInteractiveShell() # not tested
|
|
+ <sage.repl.interpreter.SageNotebookInteractiveShell object at 0x...>
|
|
+ """
|
|
+
|
|
+ def init_display_formatter(self):
|
|
+ """
|
|
+ Switch to the Sage IPython commandline rich output backend
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.interpreter import SageTerminalInteractiveShell
|
|
+ sage: SageTerminalInteractiveShell().init_display_formatter() # not tested
|
|
+ """
|
|
+ from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline
|
|
+ backend = BackendIPythonCommandline()
|
|
+ backend.get_display_manager().switch_backend(backend, shell=self)
|
|
+
|
|
+
|
|
+class SageTestShell(SageShellOverride, TerminalInteractiveShell):
|
|
+ """
|
|
+ Test Shell
|
|
+
|
|
+ Care must be taken in these doctests to quit the test shell in
|
|
+ order to switch back the rich output display backend to the
|
|
+ doctest backend.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.interpreter import get_test_shell
|
|
+ sage: shell = get_test_shell(); shell
|
|
+ <sage.repl.interpreter.SageTestShell object at 0x...>
|
|
+ sage: shell.quit()
|
|
+ """
|
|
+
|
|
+ def init_display_formatter(self):
|
|
+ """
|
|
+ Switch to the Sage IPython commandline rich output backend
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.interpreter import get_test_shell
|
|
+ sage: shell = get_test_shell(); shell
|
|
+ <sage.repl.interpreter.SageTestShell object at 0x...>
|
|
+ sage: shell.quit()
|
|
+ sage: shell.init_display_formatter()
|
|
+ sage: shell.quit()
|
|
+ """
|
|
+ from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline
|
|
+ self._ipython_backend = backend = BackendIPythonCommandline()
|
|
+ self._display_manager = backend.get_display_manager()
|
|
+ self._doctest_backend = self._display_manager.switch_backend(backend, shell=self)
|
|
+
|
|
+ def quit(self):
|
|
+ """
|
|
+ Quit the test shell.
|
|
+
|
|
+ To make the test shell as realistic as possible, we switch to
|
|
+ the
|
|
+ :class:`~sage.repl.rich_output.backend_ipython.BackendIPythonCommandline`
|
|
+ display backend. This method restores the previous display
|
|
+ backend, which is the
|
|
+ :class:`~sage.repl.rich_output.backend_doctest.BackendDoctest`
|
|
+ during doctests.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.interpreter import get_test_shell
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: get_display_manager()
|
|
+ The Sage display manager using the doctest backend
|
|
+
|
|
+ sage: shell = get_test_shell()
|
|
+ sage: get_display_manager()
|
|
+ The Sage display manager using the IPython command line backend
|
|
+
|
|
+ sage: shell.quit()
|
|
+ sage: get_display_manager()
|
|
+ The Sage display manager using the doctest backend
|
|
+ """
|
|
+ self._display_manager.switch_backend(self._doctest_backend)
|
|
+
|
|
+ def _restart(self):
|
|
+ """
|
|
+ Restart the test shell (after :meth:`quit`).
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.interpreter import get_test_shell
|
|
+ sage: shell = get_test_shell()
|
|
+ sage: shell.quit()
|
|
+ sage: shell._restart()
|
|
+ sage: shell.quit()
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: get_display_manager()
|
|
+ The Sage display manager using the doctest backend
|
|
+ """
|
|
+ self._display_manager.switch_backend(self._ipython_backend, shell=self)
|
|
+
|
|
+ def run_cell(self, *args, **kwds):
|
|
+ """
|
|
+ Run IPython cell
|
|
+
|
|
+ Starting with IPython-3.0, this returns an success/failure
|
|
+ information. Since it is more convenient for doctests, we
|
|
+ ignore it.
|
|
+
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.interpreter import get_test_shell
|
|
+ sage: shell = get_test_shell()
|
|
+ sage: rc = shell.run_cell('1/0')
|
|
+ ---------------------------------------------------------------------------
|
|
+ ZeroDivisionError Traceback (most recent call last)
|
|
+ ...
|
|
+ ZeroDivisionError: Rational division by zero
|
|
+ sage: rc is None
|
|
+ True
|
|
+ sage: shell.quit()
|
|
+ """
|
|
+ rc = super(SageTestShell, self).run_cell(*args, **kwds)
|
|
+
|
|
+
|
|
+###################################################################
|
|
+# Default configuration
|
|
+###################################################################
|
|
+
|
|
+DEFAULT_SAGE_CONFIG = Config(
|
|
+ PromptManager = Config(
|
|
+ in_template = 'sage: ',
|
|
+ in2_template = '....: ',
|
|
+ justify = False,
|
|
+ out_template = ''),
|
|
+ TerminalIPythonApp = Config(
|
|
+ display_banner = False,
|
|
+ verbose_crash = True,
|
|
+ test_shell = False,
|
|
+ shell_class = SageTerminalInteractiveShell,
|
|
+ ),
|
|
+ InteractiveShell = Config(
|
|
+ ast_node_interactivity = 'all',
|
|
+ colors = 'LightBG' if sys.stdout.isatty() else 'NoColor',
|
|
+ confirm_exit = False,
|
|
+ separate_in = ''),
|
|
+ InteractiveShellApp = Config(extensions=[SAGE_EXTENSION]),
|
|
+)
|
|
|
|
|
|
###################################################################
|
|
@@ -274,6 +442,7 @@ def SagePreparseTransformer(line):
|
|
File "<string>", line unknown
|
|
SyntaxError: Mismatched ']'
|
|
<BLANKLINE>
|
|
+ sage: shell.quit()
|
|
"""
|
|
if _do_preparse and not line.startswith('%'):
|
|
return preparse(line)
|
|
@@ -322,6 +491,7 @@ def SagePromptTransformer():
|
|
|
|
sage: shell.run_cell(' sage: 1+1')
|
|
2
|
|
+ sage: shell.quit()
|
|
"""
|
|
_sage_prompt_re = re.compile(r'^(\s*(:?sage: |\.\.\.\.: ))+')
|
|
return _strip_prompts(_sage_prompt_re)
|
|
@@ -454,6 +624,7 @@ def interface_shell_embed(interface):
|
|
sage: shell = interface_shell_embed(gap)
|
|
sage: shell.run_cell('List( [1..10], IsPrime )')
|
|
[ false, true, true, false, true, false, true, false, false, false ]
|
|
+ <IPython.core.interactiveshell.ExecutionResult object at 0x...>
|
|
"""
|
|
try:
|
|
cfg = copy.deepcopy(get_ipython().config)
|
|
@@ -483,13 +654,20 @@ def get_test_shell():
|
|
Returns a IPython shell that can be used in testing the functions
|
|
in this module.
|
|
|
|
- :returns: an IPython shell
|
|
+ OUTPUT:
|
|
+
|
|
+ An IPython shell
|
|
|
|
EXAMPLES::
|
|
|
|
sage: from sage.repl.interpreter import get_test_shell
|
|
sage: shell = get_test_shell(); shell
|
|
- <sage.repl.interpreter.SageTerminalInteractiveShell object at 0x...>
|
|
+ <sage.repl.interpreter.SageTestShell object at 0x...>
|
|
+ sage: shell.parent.shell_class
|
|
+ <class 'sage.repl.interpreter.SageTestShell'>
|
|
+ sage: shell.parent.test_shell
|
|
+ True
|
|
+ sage: shell.quit()
|
|
|
|
TESTS:
|
|
|
|
@@ -501,9 +679,17 @@ def get_test_shell():
|
|
sage: out + err
|
|
''
|
|
"""
|
|
- app = SageTerminalApp.instance(config=copy.deepcopy(DEFAULT_SAGE_CONFIG))
|
|
+ config = copy.deepcopy(DEFAULT_SAGE_CONFIG)
|
|
+ config.TerminalIPythonApp.test_shell = True
|
|
+ config.TerminalIPythonApp.shell_class = SageTestShell
|
|
+ app = SageTerminalApp.instance(config=config)
|
|
if app.shell is None:
|
|
app.initialize(argv=[])
|
|
+ else:
|
|
+ try:
|
|
+ app.shell._restart()
|
|
+ except AttributeError:
|
|
+ pass
|
|
# overwrite the default (console + graphics) formatter with the plain text one
|
|
import sage.repl.display.formatter as formatter
|
|
app.shell.display_formatter.formatters['text/plain'] = (
|
|
@@ -548,7 +734,11 @@ class SageCrashHandler(IPAppCrashHandler):
|
|
class SageTerminalApp(TerminalIPythonApp):
|
|
name = u'Sage'
|
|
crash_handler_class = SageCrashHandler
|
|
- test_shell = False
|
|
+
|
|
+ test_shell = Bool(False, config=True,
|
|
+ help='Whether the shell is a test shell')
|
|
+ shell_class = Type(InteractiveShell, config=True,
|
|
+ help='Type of the shell')
|
|
|
|
def load_config_file(self, *args, **kwds):
|
|
r"""
|
|
@@ -591,13 +781,12 @@ class SageTerminalApp(TerminalIPythonApp):
|
|
EXAMPLES::
|
|
|
|
sage: from sage.repl.interpreter import SageTerminalApp, DEFAULT_SAGE_CONFIG
|
|
- sage: app = SageTerminalApp(config=DEFAULT_SAGE_CONFIG)
|
|
- sage: app.initialize(argv=[]) # indirect doctest
|
|
+ sage: app = SageTerminalApp.instance()
|
|
sage: app.shell
|
|
- <sage.repl.interpreter.SageTerminalInteractiveShell object at 0x...>
|
|
+ <sage.repl.interpreter.SageTestShell object at 0x...>
|
|
"""
|
|
# Shell initialization
|
|
- self.shell = SageTerminalInteractiveShell.instance(
|
|
+ self.shell = self.shell_class.instance(
|
|
parent=self,
|
|
config=self.config,
|
|
display_banner=False,
|
|
diff --git a/src/sage/repl/ipython_extension.py b/src/sage/repl/ipython_extension.py
|
|
index 8cb2d5f..f924006 100644
|
|
--- a/src/sage/repl/ipython_extension.py
|
|
+++ b/src/sage/repl/ipython_extension.py
|
|
@@ -53,6 +53,7 @@ In contrast, input to the ``%time`` magic command is preparsed::
|
|
CPU times: user ...
|
|
Wall time: ...
|
|
2 * 3^3 * 11
|
|
+ sage: shell.quit()
|
|
"""
|
|
|
|
from IPython.core.magic import Magics, magics_class, line_magic
|
|
@@ -81,6 +82,7 @@ class SageMagics(Magics):
|
|
PROFILE: interrupts/evictions/bytes = ...
|
|
Using local file ...
|
|
Using local file ...
|
|
+ sage: shell.quit()
|
|
"""
|
|
import sage.misc.gperftools
|
|
sage.misc.gperftools.crun(s, evaluator=self.shell.ex)
|
|
@@ -106,6 +108,7 @@ class SageMagics(Magics):
|
|
sage: shell.run_cell('%runfile '+tmp)
|
|
sage: shell.run_cell('a')
|
|
2
|
|
+ sage: shell.quit()
|
|
"""
|
|
return self.shell.ex(load_wrap(s, attach=False))
|
|
|
|
@@ -146,6 +149,7 @@ class SageMagics(Magics):
|
|
sage: shell.run_cell('attached_files()')
|
|
[]
|
|
sage: os.remove(tmp)
|
|
+ sage: shell.quit()
|
|
"""
|
|
return self.shell.ex(load_wrap(s, attach=True))
|
|
|
|
@@ -215,7 +219,8 @@ class SageMagics(Magics):
|
|
|
|
Then when you want return in 'textual mode'::
|
|
|
|
- sage: shell.run_cell('%display simple')
|
|
+ sage: shell.run_cell('%display text plain')
|
|
+ sage: shell.run_cell('%display plain') # shortcut for "text plain"
|
|
sage: shell.run_cell('sum(i^2*x^i, i, 0, 10)')
|
|
100*x^10 + 81*x^9 + 64*x^8 + 49*x^7 + 36*x^6 + 25*x^5 + 16*x^4 + 9*x^3 + 4*x^2 + x
|
|
|
|
@@ -248,32 +253,38 @@ class SageMagics(Magics):
|
|
As yet another option, typeset mode. This is used in the emacs
|
|
interface::
|
|
|
|
- sage: shell.run_cell('%display typeset')
|
|
+ sage: shell.run_cell('%display text latex')
|
|
sage: shell.run_cell('1/2')
|
|
- <html><script type="math/tex">...\frac{1}{2}</script></html>
|
|
+ \newcommand{\Bold}[1]{\mathbf{#1}}\frac{1}{2}
|
|
|
|
Switch back::
|
|
|
|
- sage: shell.run_cell('%display simple')
|
|
+ sage: shell.run_cell('%display default')
|
|
+
|
|
+ Switch graphics to default to vector or raster graphics file
|
|
+ formats::
|
|
+
|
|
+ sage: shell.run_cell('%display graphics vector')
|
|
|
|
TESTS::
|
|
|
|
sage: shell.run_cell('%display invalid_mode')
|
|
- ---------------------------------------------------------------------------
|
|
- ValueError Traceback (most recent call last)
|
|
- ...
|
|
- ValueError: invalid mode set
|
|
+ value must be unset (None) or one of ('plain', 'ascii_art', 'latex'), got invalid_mode
|
|
+ sage: shell.quit()
|
|
"""
|
|
+ from sage.repl.rich_output import get_display_manager
|
|
+ dm = get_display_manager()
|
|
args = args.strip().split()
|
|
if not args:
|
|
- # toggle
|
|
- self._magic_display_status = ('ascii_art' if
|
|
- self._magic_display_status == 'simple' else 'simple')
|
|
- else:
|
|
- mode = args[0]
|
|
- self._magic_display_status = mode
|
|
-
|
|
- if self._magic_display_status == 'ascii_art' and len(args) > 1:
|
|
+ print(dm.preferences)
|
|
+ return
|
|
+ arg0 = args[0]
|
|
+ # deprecated values
|
|
+ if arg0 == 'simple':
|
|
+ dm.preferences.text = 'plain'
|
|
+ elif arg0 == 'typeset':
|
|
+ dm.preferences.text = 'latex'
|
|
+ elif arg0 == 'ascii_art' and len(args) > 1:
|
|
try:
|
|
max_width = int(args[1])
|
|
except ValueError:
|
|
@@ -283,9 +294,33 @@ class SageMagics(Magics):
|
|
"ascii_art max width must be a positive integer")
|
|
import sage.misc.ascii_art as ascii_art
|
|
ascii_art.MAX_WIDTH = max_width
|
|
+ dm.preferences.text = 'ascii_art'
|
|
+ # Unset
|
|
+ elif arg0 in ['default', 'None']: # un-stringify "%display None"
|
|
+ dm.preferences.text = None
|
|
+ dm.preferences.graphics = None
|
|
+ # Normal argument handling
|
|
+ elif arg0 == 'text' and len(args) == 1:
|
|
+ print(dm.preferences.text)
|
|
+ elif arg0 == 'text' and len(args) == 2:
|
|
+ try:
|
|
+ dm.preferences.text = args[1]
|
|
+ except ValueError as err:
|
|
+ print(err) # do not show traceback
|
|
+ elif arg0 == 'graphics' and len(args) == 1:
|
|
+ print(dm.preferences.graphics)
|
|
+ elif arg0 == 'graphics' and len(args) == 2:
|
|
+ try:
|
|
+ dm.preferences.graphics = args[1]
|
|
+ except ValueError as err:
|
|
+ print(err) # do not show traceback
|
|
+ # If all else fails: assume text
|
|
+ else:
|
|
+ try:
|
|
+ dm.preferences.text = arg0
|
|
+ except ValueError as err:
|
|
+ print(err) # do not show traceback
|
|
|
|
- self.shell.display_formatter.formatters['text/plain'].set_display(
|
|
- self._magic_display_status)
|
|
|
|
class SageCustomizations(object):
|
|
|
|
@@ -300,7 +335,7 @@ class SageCustomizations(object):
|
|
|
|
import sage.repl.display.formatter as formatter
|
|
self.shell.display_formatter.formatters['text/plain'] = (
|
|
- formatter.SageConsoleTextFormatter(config=shell.config))
|
|
+ formatter.SagePlainTextFormatter(config=shell.config))
|
|
|
|
import sage.misc.edit_module as edit_module
|
|
self.shell.set_hook('editor', edit_module.edit_devel)
|
|
diff --git a/src/sage/repl/ipython_kernel/__init__.py b/src/sage/repl/ipython_kernel/__init__.py
|
|
new file mode 100644
|
|
index 0000000..e69de29
|
|
--- /dev/null
|
|
+++ b/src/sage/repl/ipython_kernel/__init__.py
|
|
diff --git a/src/sage/repl/ipython_kernel/__main__.py b/src/sage/repl/ipython_kernel/__main__.py
|
|
new file mode 100644
|
|
index 0000000..ee2d10f
|
|
--- /dev/null
|
|
+++ b/src/sage/repl/ipython_kernel/__main__.py
|
|
@@ -0,0 +1,4 @@
|
|
+print('Sage IPython Kernel')
|
|
+from IPython.kernel.zmq.kernelapp import IPKernelApp
|
|
+from sage.repl.ipython_kernel.kernel import SageKernel
|
|
+IPKernelApp.launch_instance(kernel_class=SageKernel)
|
|
diff --git a/src/sage/repl/ipython_kernel/install.py b/src/sage/repl/ipython_kernel/install.py
|
|
new file mode 100644
|
|
index 0000000..6593be6
|
|
--- /dev/null
|
|
+++ b/src/sage/repl/ipython_kernel/install.py
|
|
@@ -0,0 +1,157 @@
|
|
+"""
|
|
+Sage-Enhanced IPython Notebook
|
|
+
|
|
+.. note::
|
|
+
|
|
+ The customized Jinja2 templates for the IPython notebook are in
|
|
+ ``SAGE_LOCAL/share/sage/ext/ipython-notebook``. You may have to
|
|
+ update them as well when updating to a new IPython version.
|
|
+"""
|
|
+
|
|
+import os
|
|
+import errno
|
|
+import copy
|
|
+
|
|
+from IPython.kernel.kernelspec import (
|
|
+ get_kernel_spec, install_kernel_spec, NoSuchKernel)
|
|
+
|
|
+
|
|
+from sage.env import (
|
|
+ SAGE_ROOT, SAGE_DOC, SAGE_LOCAL, SAGE_EXTCODE,
|
|
+ SAGE_VERSION
|
|
+)
|
|
+from sage.misc.temporary_file import tmp_dir
|
|
+
|
|
+
|
|
+class SageKernelSpec(object):
|
|
+
|
|
+ def __init__(self):
|
|
+ """
|
|
+ Utility to manage Sage kernels
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.ipython_kernel.install import SageKernelSpec
|
|
+ sage: spec = SageKernelSpec()
|
|
+ sage: spec._display_name # not tested
|
|
+ 'Sage 6.6.beta2'
|
|
+ sage: spec._identifier # not tested
|
|
+ 'sage_6_6_beta2'
|
|
+ """
|
|
+ self._display_name = 'Sage {0}'.format(SAGE_VERSION)
|
|
+
|
|
+ @classmethod
|
|
+ def identifier(self):
|
|
+ return 'Sage {0}'.format(SAGE_VERSION).lower().replace(' ', '_').replace('.', '_')
|
|
+
|
|
+ def symlink(self, src, dst):
|
|
+ """
|
|
+ Symlink ``src`` to ``dst``
|
|
+
|
|
+ This is not an atomic operation.
|
|
+
|
|
+ Already-existing symlinks will be deleted, already existing
|
|
+ non-empty directories will be kept.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.ipython_kernel.install import SageKernelSpec
|
|
+ sage: spec = SageKernelSpec()
|
|
+ sage: path = tmp_dir()
|
|
+ sage: spec.symlink(os.path.join(path, 'a'), os.path.join(path, 'b'))
|
|
+ sage: os.listdir(path)
|
|
+ ['b']
|
|
+ """
|
|
+ try:
|
|
+ os.remove(dst)
|
|
+ except OSError as err:
|
|
+ if err.errno == errno.EEXIST:
|
|
+ return
|
|
+ os.symlink(src, dst)
|
|
+
|
|
+ def use_local_mathjax(self):
|
|
+ ipython_dir = os.environ['IPYTHONDIR']
|
|
+ src = os.path.join(SAGE_LOCAL, 'share', 'mathjax')
|
|
+ dst = os.path.join(ipython_dir, 'nbextensions', 'mathjax')
|
|
+ self.symlink(src, dst)
|
|
+
|
|
+ def _kernel_cmd(self):
|
|
+ return [
|
|
+ os.path.join(SAGE_ROOT, 'sage'),
|
|
+ '-python',
|
|
+ '-m', 'sage.repl.ipython_kernel',
|
|
+ '-f', '{connection_file}',
|
|
+ ]
|
|
+
|
|
+ def kernel_spec(self):
|
|
+ """
|
|
+ Return the kernel spec as Python dictionary
|
|
+ """
|
|
+ return dict(
|
|
+ argv=self._kernel_cmd(),
|
|
+ display_name=self._display_name,
|
|
+ )
|
|
+
|
|
+ def install_spec(self):
|
|
+ """
|
|
+ Install the Sage IPython kernel
|
|
+
|
|
+ It is safe to call this method multiple times, only one Sage
|
|
+ kernel spec is ever installed.
|
|
+ """
|
|
+ import json
|
|
+ temp = tmp_dir()
|
|
+ kernel_spec = os.path.join(temp, 'kernel.json')
|
|
+ with open(kernel_spec, 'w') as f:
|
|
+ json.dump(self.kernel_spec(), f)
|
|
+ identifier = self.identifier()
|
|
+ install_kernel_spec(temp, identifier, user=True, replace=True)
|
|
+ self._spec = get_kernel_spec(identifier)
|
|
+
|
|
+ def symlink_resources(self):
|
|
+ spec_dir = self._spec.resource_dir
|
|
+ path = os.path.join(SAGE_EXTCODE, 'notebook-ipython')
|
|
+ for filename in os.listdir(path):
|
|
+ self.symlink(
|
|
+ os.path.join(path, filename),
|
|
+ os.path.join(spec_dir, filename)
|
|
+ )
|
|
+ self.symlink(
|
|
+ os.path.join(SAGE_DOC, 'output', 'html', 'en'),
|
|
+ os.path.join(spec_dir, 'doc')
|
|
+ )
|
|
+
|
|
+ @classmethod
|
|
+ def update(cls):
|
|
+ instance = cls()
|
|
+ instance.use_local_mathjax()
|
|
+ instance.install_spec()
|
|
+ instance.symlink_resources()
|
|
+
|
|
+
|
|
+def have_prerequisites():
|
|
+ """
|
|
+ Check that we have all prerequisites to run the IPython notebook.
|
|
+
|
|
+ In particular, the IPython notebook requires OpenSSL whether or
|
|
+ not you are using https. See trac:`17318`.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Boolean.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.notebook_ipython import have_prerequisites
|
|
+ sage: have_prerequisites() in [True, False]
|
|
+ True
|
|
+ """
|
|
+ try:
|
|
+ from IPython.html.notebookapp import NotebookApp
|
|
+ return True
|
|
+ except ImportError as err:
|
|
+ return False
|
|
+
|
|
+
|
|
+
|
|
+
|
|
diff --git a/src/sage/repl/ipython_kernel/kernel.py b/src/sage/repl/ipython_kernel/kernel.py
|
|
new file mode 100644
|
|
index 0000000..4de4ade
|
|
--- /dev/null
|
|
+++ b/src/sage/repl/ipython_kernel/kernel.py
|
|
@@ -0,0 +1,115 @@
|
|
+"""
|
|
+The Sage ZMQ Kernel
|
|
+
|
|
+Version of the IPython kernel when running Sage inside the IPython
|
|
+notebook or remote IPython sessions.
|
|
+"""
|
|
+
|
|
+#*****************************************************************************
|
|
+# Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com>
|
|
+#
|
|
+# Distributed under the terms of the GNU General Public License (GPL)
|
|
+# as published by the Free Software Foundation; either version 2 of
|
|
+# the License, or (at your option) any later version.
|
|
+# http://www.gnu.org/licenses/
|
|
+#*****************************************************************************
|
|
+
|
|
+import sys
|
|
+from IPython.kernel.zmq.ipkernel import IPythonKernel
|
|
+from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
|
|
+from IPython.utils.traitlets import Type
|
|
+
|
|
+from sage.env import SAGE_VERSION, SAGE_EXTCODE, SAGE_DOC
|
|
+from sage.repl.interpreter import SageNotebookInteractiveShell
|
|
+from sage.repl.ipython_extension import SageCustomizations
|
|
+
|
|
+class SageZMQInteractiveShell(SageNotebookInteractiveShell, ZMQInteractiveShell):
|
|
+ pass
|
|
+
|
|
+
|
|
+class SageKernel(IPythonKernel):
|
|
+ implementation = 'sage'
|
|
+ implementation_version = SAGE_VERSION
|
|
+
|
|
+ shell_class = Type(SageZMQInteractiveShell)
|
|
+
|
|
+ def __init__(self, **kwds):
|
|
+ super(SageKernel, self).__init__(**kwds)
|
|
+ SageCustomizations(self.shell)
|
|
+
|
|
+ @property
|
|
+ def banner(self):
|
|
+ from sage.misc.banner import banner_text
|
|
+ return banner_text()
|
|
+
|
|
+ @property
|
|
+ def help_links(self):
|
|
+ from sage.repl.ipython_kernel.install import SageKernelSpec
|
|
+ identifier = SageKernelSpec.identifier()
|
|
+ kernel_url = lambda x: '/kernelspecs/{0}/{1}'.format(identifier, x)
|
|
+ return [
|
|
+ {
|
|
+ 'text': 'Sage Documentation',
|
|
+ 'url': kernel_url('doc/index.html')
|
|
+ },
|
|
+ {
|
|
+ 'text': 'Sage Tutorial',
|
|
+ 'url': kernel_url('doc/tutorial/index.html'),
|
|
+ },
|
|
+ {
|
|
+ 'text': 'Thematic Tutorials',
|
|
+ 'url': kernel_url('doc/thematic_tutorials/index.html'),
|
|
+ },
|
|
+ {
|
|
+ 'text': 'FAQs',
|
|
+ 'url': kernel_url('doc/faq/index.html'),
|
|
+ },
|
|
+ {
|
|
+ 'text': 'PREP Tutorials',
|
|
+ 'url': kernel_url('doc/prep/index.html'),
|
|
+ },
|
|
+ {
|
|
+ 'text': 'Sage Reference',
|
|
+ 'url': kernel_url('doc/reference/index.html'),
|
|
+ },
|
|
+ {
|
|
+ 'text': 'Developers Guide',
|
|
+ 'url': kernel_url('doc/developer/index.html'),
|
|
+ },
|
|
+ {
|
|
+ 'text': "Python",
|
|
+ 'url': "http://docs.python.org/%i.%i" % sys.version_info[:2],
|
|
+ },
|
|
+ {
|
|
+ 'text': "IPython",
|
|
+ 'url': "http://ipython.org/documentation.html",
|
|
+ },
|
|
+ {
|
|
+ 'text': 'Singular',
|
|
+ 'url': 'http://www.singular.uni-kl.de/Manual/latest/index.htm',
|
|
+ },
|
|
+ {
|
|
+ 'text': 'GAP',
|
|
+ 'url': 'http://gap-system.org/Manuals/doc/ref/chap0.html',
|
|
+ },
|
|
+ {
|
|
+ 'text': "NumPy",
|
|
+ 'url': "http://docs.scipy.org/doc/numpy/reference/",
|
|
+ },
|
|
+ {
|
|
+ 'text': "SciPy",
|
|
+ 'url': "http://docs.scipy.org/doc/scipy/reference/",
|
|
+ },
|
|
+ {
|
|
+ 'text': "SymPy",
|
|
+ 'url': 'http://docs.sympy.org/latest/index.html',
|
|
+ },
|
|
+ {
|
|
+ 'text': "Matplotlib",
|
|
+ 'url': "http://matplotlib.org/contents.html",
|
|
+ },
|
|
+ {
|
|
+ 'text': "Markdown",
|
|
+ 'url': "http://help.github.com/articles/github-flavored-markdown",
|
|
+ },
|
|
+ ]
|
|
diff --git a/src/sage/repl/notebook_ipython.py b/src/sage/repl/notebook_ipython.py
|
|
deleted file mode 100644
|
|
index aa59040..0000000
|
|
--- a/src/sage/repl/notebook_ipython.py
|
|
+++ /dev/null
|
|
@@ -1,128 +0,0 @@
|
|
-"""
|
|
-Sage-Enhanced IPython Notebook
|
|
-
|
|
-.. note::
|
|
-
|
|
- The customized Jinja2 templates for the IPython notebook are in
|
|
- ``SAGE_LOCAL/share/sage/ext/ipython-notebook``. You may have to
|
|
- update them as well when updating to a new IPython version.
|
|
-"""
|
|
-
|
|
-import os
|
|
-import copy
|
|
-
|
|
-from IPython import Config
|
|
-try:
|
|
- # Fails if we do not have ssl
|
|
- from IPython.html.notebookapp import NotebookApp
|
|
-except ImportError:
|
|
- NotebookApp = object
|
|
-
|
|
-
|
|
-from sage.env import DOT_SAGE, SAGE_EXTCODE, SAGE_DOC
|
|
-from sage.repl.interpreter import (
|
|
- SageCrashHandler, DEFAULT_SAGE_CONFIG, SageInteractiveShell,
|
|
-)
|
|
-
|
|
-
|
|
-# The notebook Jinja2 templates and static files
|
|
-TEMPLATE_PATH = os.path.join(SAGE_EXTCODE, 'notebook-ipython', 'templates')
|
|
-STATIC_PATH = os.path.join(SAGE_EXTCODE, 'notebook-ipython', 'static')
|
|
-DOCS_PATH = os.path.join(SAGE_DOC, 'output', 'html', 'en')
|
|
-
|
|
-
|
|
-# Note: sage.repl.interpreter.DEFAULT_SAGE_CONFIG will be applied, too
|
|
-DEFAULT_SAGE_NOTEBOOK_CONFIG = Config(
|
|
- SageNotebookApp = Config(
|
|
- # log_level = 'DEBUG', # if you want more logs
|
|
- # open_browser = False, # if you want to avoid browser restart
|
|
- webapp_settings = Config(
|
|
- template_path = TEMPLATE_PATH,
|
|
- ),
|
|
- extra_static_paths = [STATIC_PATH, DOCS_PATH],
|
|
- ),
|
|
-)
|
|
-
|
|
-
|
|
-def have_prerequisites():
|
|
- """
|
|
- Check that we have all prerequisites to run the IPython notebook.
|
|
-
|
|
- In particular, the IPython notebook requires OpenSSL whether or
|
|
- not you are using https. See trac:`17318`.
|
|
-
|
|
- OUTPUT:
|
|
-
|
|
- Boolean.
|
|
-
|
|
- EXAMPLES::
|
|
-
|
|
- sage: from sage.repl.notebook_ipython import have_prerequisites
|
|
- sage: have_prerequisites() in [True, False]
|
|
- True
|
|
- """
|
|
- return NotebookApp != object # that is, import worked
|
|
-
|
|
-
|
|
-class SageNotebookApp(NotebookApp):
|
|
- name = u'sage-notebook-ipython'
|
|
- crash_handler_class = SageCrashHandler
|
|
-
|
|
- def load_config_file(self, *args, **kwds):
|
|
- r"""
|
|
- Merges a config file with the default sage notebook config.
|
|
-
|
|
- EXAMPLES::
|
|
-
|
|
- sage: from sage.misc.temporary_file import tmp_dir
|
|
- sage: from sage.repl.notebook_ipython import SageNotebookApp
|
|
- sage: d = tmp_dir()
|
|
- sage: IPYTHONDIR = os.environ['IPYTHONDIR']
|
|
- sage: os.environ['IPYTHONDIR'] = d
|
|
- sage: app = SageNotebookApp() # optional - ssl
|
|
- sage: app.load_config_file() # optional - ssl, random output
|
|
- 2014-09-16 23:57:35.6 [SageNotebookApp] Created profile dir:
|
|
- u'/home/vbraun/.sage/temp/desktop.localdomain/1490/dir_ZQupP5/profile_default'
|
|
- sage: app.notebook_dir # random output, optional - ssl
|
|
- u'/home/vbraun/'
|
|
- sage: os.environ['IPYTHONDIR'] = IPYTHONDIR
|
|
- """
|
|
- super(SageNotebookApp, self).load_config_file(*args, **kwds)
|
|
- newconfig = copy.deepcopy(DEFAULT_SAGE_CONFIG)
|
|
- newconfig.merge(DEFAULT_SAGE_NOTEBOOK_CONFIG)
|
|
- newconfig.merge(self.config)
|
|
- self.config = newconfig
|
|
-
|
|
- def init_kernel_argv(self):
|
|
- """
|
|
- Construct the kernel arguments
|
|
-
|
|
- The kernel is running in a separate process, so it does not
|
|
- see our config dictionary. Any options need to be passed
|
|
- through the command line interface.
|
|
-
|
|
- EXAMPLES::
|
|
-
|
|
- sage: from sage.repl.notebook_ipython import SageNotebookApp
|
|
- sage: d = tmp_dir()
|
|
- sage: IPYTHONDIR = os.environ['IPYTHONDIR']
|
|
- sage: os.environ['IPYTHONDIR'] = d
|
|
- sage: app = SageNotebookApp() # optional - ssl
|
|
- sage: app.kernel_argv # optional - ssl
|
|
- []
|
|
- sage: app.init_kernel_argv() # optional - ssl, random output
|
|
- 2014-09-16 23:57:35.6 [SageNotebookApp] Created profile dir:
|
|
- u'/home/vbraun/.sage/temp/desktop.localdomain/1490/dir_ZQupP5/profile_default'
|
|
- sage: app.kernel_argv # optional - ssl
|
|
- [u"--IPKernelApp.parent_appname='sage-notebook-ipython'",
|
|
- '--profile-dir',
|
|
- u'/.../profile_default',
|
|
- u'--IPKernelApp.kernel_class=sage.repl.zmq_kernel.SageKernel',
|
|
- u'--IPKernelApp.extra_extension=sage']
|
|
- sage: os.environ['IPYTHONDIR'] = IPYTHONDIR
|
|
- """
|
|
- super(SageNotebookApp, self).init_kernel_argv()
|
|
- self.kernel_argv.append(
|
|
- u'--IPKernelApp.kernel_class=sage.repl.zmq_kernel.SageKernel')
|
|
- self.kernel_argv.append(
|
|
- u'--IPKernelApp.extra_extension=sage')
|
|
diff --git a/src/sage/repl/rich_output/__init__.py b/src/sage/repl/rich_output/__init__.py
|
|
new file mode 100644
|
|
index 0000000..5b028cd
|
|
--- /dev/null
|
|
+++ b/src/sage/repl/rich_output/__init__.py
|
|
@@ -0,0 +1,5 @@
|
|
+# -*- encoding: utf-8 -*-
|
|
+
|
|
+from .display_manager import get_display_manager
|
|
+
|
|
+
|
|
diff --git a/src/sage/repl/rich_output/backend_base.py b/src/sage/repl/rich_output/backend_base.py
|
|
new file mode 100644
|
|
index 0000000..7f4f127
|
|
--- /dev/null
|
|
+++ b/src/sage/repl/rich_output/backend_base.py
|
|
@@ -0,0 +1,542 @@
|
|
+# -*- encoding: utf-8 -*-
|
|
+r"""
|
|
+Base class for Backends
|
|
+
|
|
+The display backends are the commandline, the SageNB notebook, the
|
|
+ipython notebook, the Emacs sage mode, the Sage doctester, .... All of
|
|
+these have different capabilities for what they can display.
|
|
+
|
|
+To implement a new display backend, you need to subclass
|
|
+:class:`BackendBase`. All backend-specific handlig of rich output
|
|
+should be in :meth:`~BackendBase.displayhook` and
|
|
+:meth:`~BackendBase.display_immediately`. See :class:`BackendSimple`
|
|
+for an absolutely minimal example of a functioning backend.
|
|
+
|
|
+You declare the types of rich output that your backend can handle in
|
|
+:meth:`~BackendBase.supported_output`. There are two ways to then
|
|
+display specific output types in your own backend.
|
|
+
|
|
+* Directly use one of the existing output containers listed in
|
|
+ :mod:`sage.repl.rich_output.output_catalog`. That is, test for the
|
|
+ rich output type in :meth:`~BackendBase.display_immediately` and
|
|
+ handle it.
|
|
+
|
|
+* Subclass the rich output container to attach your backend-specific
|
|
+ functionality. Then :meth:`~BackendBase.display_immediately` will
|
|
+ receive instances of your subclass. See
|
|
+ :class:`~sage.repl.rich_output.backend_test.BackendTest` for an
|
|
+ example of how this is done.
|
|
+
|
|
+You can also mix both ways of implementing different rich output types.
|
|
+
|
|
+EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_base import BackendSimple
|
|
+ sage: backend = BackendSimple()
|
|
+ sage: plain_text = backend.plain_text_formatter(range(10)); plain_text
|
|
+ OutputPlainText container
|
|
+ sage: backend.displayhook(plain_text, plain_text)
|
|
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
+"""
|
|
+
|
|
+#*****************************************************************************
|
|
+# Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com>
|
|
+#
|
|
+# Distributed under the terms of the GNU General Public License (GPL)
|
|
+# as published by the Free Software Foundation; either version 2 of
|
|
+# the License, or (at your option) any later version.
|
|
+# http://www.gnu.org/licenses/
|
|
+#*****************************************************************************
|
|
+
|
|
+from sage.structure.sage_object import SageObject
|
|
+
|
|
+
|
|
+class BackendBase(SageObject):
|
|
+
|
|
+ def _repr_(self):
|
|
+ """
|
|
+ Return string representation of the backend
|
|
+
|
|
+ Every backend must implement this method.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_base import BackendBase
|
|
+ sage: backend = BackendBase()
|
|
+ sage: backend._repr_()
|
|
+ Traceback (most recent call last):
|
|
+ ...
|
|
+ NotImplementedError: derived classes must implement this method
|
|
+ """
|
|
+ raise NotImplementedError('derived classes must implement this method')
|
|
+
|
|
+ def get_display_manager(self):
|
|
+ """
|
|
+ Return the display manager singleton
|
|
+
|
|
+ This is a convenience method to access the display manager
|
|
+ singleton.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ The unique
|
|
+ :class:`~sage.repl.rich_output.display_manager.DisplayManager`
|
|
+ instance.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_base import BackendBase
|
|
+ sage: backend = BackendBase()
|
|
+ sage: backend.get_display_manager()
|
|
+ The Sage display manager using the doctest backend
|
|
+ """
|
|
+ from sage.repl.rich_output import get_display_manager
|
|
+ return get_display_manager()
|
|
+
|
|
+ def install(self, **kwds):
|
|
+ """
|
|
+ Hook that will be called once before the backend is used for the
|
|
+ first time.
|
|
+
|
|
+ The default implementation does nothing.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``kwds`` -- optional keyword arguments that are passed
|
|
+ through by the
|
|
+ :meth:`~sage.repl.rich_output.display_manager.DisplayManager.switch_backend`
|
|
+ method.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_base import BackendBase
|
|
+ sage: backend = BackendBase()
|
|
+ sage: backend.install()
|
|
+ """
|
|
+ pass
|
|
+
|
|
+ def uninstall(self):
|
|
+ """
|
|
+ Hook that will be called once right before the backend is removed.
|
|
+
|
|
+ The default implementation does nothing.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_base import BackendBase
|
|
+ sage: backend = BackendBase()
|
|
+ sage: backend.uninstall()
|
|
+ """
|
|
+ pass
|
|
+
|
|
+ def default_preferences(self):
|
|
+ """
|
|
+ Return the backend's display preferences
|
|
+
|
|
+ Override this method to change the default preferences when
|
|
+ using your backend.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Instance of
|
|
+ :class:`~sage.repl.rich_output.preferences.DisplayPreferences`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_base import BackendBase
|
|
+ sage: backend = BackendBase()
|
|
+ sage: backend.default_preferences()
|
|
+ Display preferences:
|
|
+ * graphics is not specified
|
|
+ * text is not specified
|
|
+ """
|
|
+ from sage.repl.rich_output.preferences import DisplayPreferences
|
|
+ return DisplayPreferences()
|
|
+
|
|
+ def supported_output(self):
|
|
+ """
|
|
+ Return the outputs that are supported by the backend.
|
|
+
|
|
+ Subclasses must implement this method.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Iterable of output container classes, that is, subclass of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputBase`). May
|
|
+ be a list/tuple/set/frozenset. The order is ignored. Only used
|
|
+ internally by the display manager.
|
|
+
|
|
+ You may return backend-specific subclasses of existing output
|
|
+ containers. This allows you to attach backend-specifc
|
|
+ functionality to the output container.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_base import BackendBase
|
|
+ sage: backend = BackendBase()
|
|
+ sage: backend.supported_output()
|
|
+ Traceback (most recent call last):
|
|
+ ...
|
|
+ NotImplementedError: derived classes must implement this method
|
|
+ """
|
|
+ raise NotImplementedError('derived classes must implement this method')
|
|
+
|
|
+ def max_width(self):
|
|
+ """
|
|
+ Return the number of characters that fit into one output line
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Integer.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_base import BackendBase
|
|
+ sage: backend = BackendBase()
|
|
+ sage: backend.max_width()
|
|
+ 79
|
|
+ """
|
|
+ return 79
|
|
+
|
|
+ def newline(self):
|
|
+ r"""
|
|
+ Return the newline string.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String for starting a new line of output.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_base import BackendBase
|
|
+ sage: backend = BackendBase()
|
|
+ sage: backend.newline()
|
|
+ '\n'
|
|
+ """
|
|
+ return '\n'
|
|
+
|
|
+ def _apply_pretty_printer(self, pretty_printer_class, obj):
|
|
+ """
|
|
+ Helper method to format ``obj`` as text
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``pretty_printer_class`` -- subclass of
|
|
+ :class:`sage.repl.display.pretty_print.SagePrettyPrinter`.
|
|
+
|
|
+ - ``obj`` -- anything.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_base import BackendBase
|
|
+ sage: backend = BackendBase()
|
|
+ sage: from sage.repl.display.pretty_print import SagePrettyPrinter
|
|
+ sage: backend._apply_pretty_printer(SagePrettyPrinter, 1/2)
|
|
+ '1/2'
|
|
+ """
|
|
+ import StringIO
|
|
+ stream = StringIO.StringIO()
|
|
+ printer = pretty_printer_class(
|
|
+ stream, self.max_width(), self.newline())
|
|
+ printer.pretty(obj)
|
|
+ printer.flush()
|
|
+ return stream.getvalue()
|
|
+
|
|
+ def plain_text_formatter(self, obj):
|
|
+ r"""
|
|
+ Hook to override how plain text is being formatted.
|
|
+
|
|
+ If the object does not have a ``_rich_repr_`` method, or if it
|
|
+ does not return a rich output object
|
|
+ (:class:`~sage.repl.rich_output.output_basic.OutputBase`),
|
|
+ then this method is used to generate plain text output.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``obj`` -- anything.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Instance of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputPlainText`
|
|
+ containing the string representation of the object.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_base import BackendBase
|
|
+ sage: backend = BackendBase()
|
|
+ sage: out = backend.plain_text_formatter(range(30))
|
|
+ sage: out
|
|
+ OutputPlainText container
|
|
+ sage: out.text
|
|
+ buffer containing 139 bytes
|
|
+ sage: out.text.get()
|
|
+ '[0,\n 1,\n 2,\n 3,\n 4,\n 5,\n 6,\n 7,\n 8,\n 9,\n
|
|
+ 10,\n 11,\n 12,\n 13,\n 14,\n 15,\n 16,\n 17,\n 18,\n
|
|
+ 19,\n 20,\n 21,\n 22,\n 23,\n 24,\n 25,\n 26,\n 27,\n
|
|
+ 28,\n 29]'
|
|
+ """
|
|
+ from sage.repl.display.pretty_print import SagePrettyPrinter
|
|
+ plain_text = self._apply_pretty_printer(SagePrettyPrinter, obj)
|
|
+ from sage.repl.rich_output.output_basic import OutputPlainText
|
|
+ return OutputPlainText(plain_text)
|
|
+
|
|
+ def ascii_art_formatter(self, obj):
|
|
+ """
|
|
+ Hook to override how ascii art is being formatted.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``obj`` -- anything.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Instance of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputAsciiArt`
|
|
+ containing the ascii art string representation of the object.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_base import BackendBase
|
|
+ sage: backend = BackendBase()
|
|
+ sage: out = backend.ascii_art_formatter(range(30))
|
|
+ sage: out
|
|
+ OutputAsciiArt container
|
|
+ sage: out.ascii_art
|
|
+ buffer containing 228 bytes
|
|
+ sage: print(out.ascii_art.get())
|
|
+ [
|
|
+ [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
|
|
+ <BLANKLINE>
|
|
+ ]
|
|
+ 22, 23, 24, 25, 26, 27, 28, 29 ]
|
|
+ """
|
|
+ from sage.repl.display.pretty_print import AsciiArtPrettyPrinter
|
|
+ ascii_art = self._apply_pretty_printer(AsciiArtPrettyPrinter, obj)
|
|
+ from sage.repl.rich_output.output_basic import OutputAsciiArt
|
|
+ return OutputAsciiArt(ascii_art)
|
|
+
|
|
+ def latex_formatter(self, obj):
|
|
+ r"""
|
|
+ Hook to override how Latex is being formatted.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``obj`` -- anything.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Instance of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputLatex`
|
|
+ containing the latex string representation of the object.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_base import BackendBase
|
|
+ sage: backend = BackendBase()
|
|
+ sage: out = backend.latex_formatter(1/2)
|
|
+ sage: out
|
|
+ OutputLatex container
|
|
+ sage: out.latex
|
|
+ buffer containing 45 bytes
|
|
+ sage: out.latex.get()
|
|
+ '\\newcommand{\\Bold}[1]{\\mathbf{#1}}\\frac{1}{2}'
|
|
+ sage: out.mathjax()
|
|
+ '<html><script type="math/tex; mode=display">\\newcommand{\\Bold}[1]{\\mathbf{#1}}\\frac{1}{2}</script></html>'
|
|
+ """
|
|
+ from sage.misc.latex import MathJax
|
|
+ mathjax = MathJax().eval(obj, mode='plain', combine_all=True)
|
|
+ from sage.repl.rich_output.output_basic import OutputLatex
|
|
+ return OutputLatex(str(mathjax))
|
|
+
|
|
+ def set_underscore_variable(self, obj):
|
|
+ """
|
|
+ Set the ``_`` builtin variable.
|
|
+
|
|
+ By default, this sets the special ``_`` variable. Backends
|
|
+ that organize the history differently (e.g. IPython) can
|
|
+ override this method.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``obj`` -- result of the most recent evaluation.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_base import BackendBase
|
|
+ sage: backend = BackendBase()
|
|
+ sage: backend.set_underscore_variable(123)
|
|
+ sage: _
|
|
+ 123
|
|
+
|
|
+ sage: 'foo'
|
|
+ 'foo'
|
|
+ sage: _ # indirect doctest
|
|
+ 'foo'
|
|
+ """
|
|
+ import __builtin__
|
|
+ __builtin__._ = obj
|
|
+
|
|
+ def displayhook(self, plain_text, rich_output):
|
|
+ """
|
|
+ Backend implementation of the displayhook
|
|
+
|
|
+ The value of the last statement on a REPL input line or
|
|
+ notebook cell are usually handed to the Python displayhook and
|
|
+ shown on screen. By overriding this method you define how
|
|
+ your backend handles output. The difference to the usual
|
|
+ displayhook is that Sage already converted the value to the
|
|
+ most suitable rich output container.
|
|
+
|
|
+ Derived classes must implement this method.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``plain_text`` -- instance of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputPlainText`. The
|
|
+ plain text version of the output.
|
|
+
|
|
+ - ``rich_output`` -- instance of an output container class
|
|
+ (subclass of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputBase`). Guaranteed
|
|
+ to be one of the output containers returned from
|
|
+ :meth:`supported_output`, possibly the same as
|
|
+ ``plain_text``.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ This method may return something, which is then returned from
|
|
+ the display manager's
|
|
+ :meth:`~sage.repl.rich_output.display_manager.DisplayManager.displayhook`
|
|
+ method.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_basic import OutputPlainText
|
|
+ sage: plain_text = OutputPlainText.example()
|
|
+ sage: from sage.repl.rich_output.backend_base import BackendBase
|
|
+ sage: backend = BackendBase()
|
|
+ sage: backend.displayhook(plain_text, plain_text)
|
|
+ Traceback (most recent call last):
|
|
+ ...
|
|
+ NotImplementedError: derived classes must implement this method
|
|
+ """
|
|
+ return self.display_immediately(plain_text, rich_output)
|
|
+
|
|
+ def display_immediately(self, plain_text, rich_output):
|
|
+ """
|
|
+ Show output without going back to the command line prompt.
|
|
+
|
|
+ This method is similar to the rich output :meth:`displayhook`,
|
|
+ except that it can be invoked at any time. Typically, it ends
|
|
+ up being called by :meth:`sage.plot.graphics.Graphics.show`.
|
|
+
|
|
+ Derived classes must implement this method.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ Same as :meth:`displayhook`.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ This method may return something so you can implement
|
|
+ :meth:`displayhook` by calling this method. However, when
|
|
+ called by the display manager any potential return value is
|
|
+ discarded: There is no way to return anything without
|
|
+ returning to the command prompt.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_basic import OutputPlainText
|
|
+ sage: plain_text = OutputPlainText.example()
|
|
+ sage: from sage.repl.rich_output.backend_base import BackendBase
|
|
+ sage: backend = BackendBase()
|
|
+ sage: backend.display_immediately(plain_text, plain_text)
|
|
+ Traceback (most recent call last):
|
|
+ ...
|
|
+ NotImplementedError: derived classes must implement this method
|
|
+ """
|
|
+ raise NotImplementedError('derived classes must implement this method')
|
|
+
|
|
+
|
|
+class BackendSimple(BackendBase):
|
|
+ """
|
|
+ Simple Backend
|
|
+
|
|
+ This backend only supports plain text.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_base import BackendSimple
|
|
+ sage: BackendSimple()
|
|
+ simple
|
|
+ """
|
|
+
|
|
+ def _repr_(self):
|
|
+ r"""
|
|
+ Return string representation of the backend
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_base import BackendSimple
|
|
+ sage: backend = BackendSimple()
|
|
+ sage: backend._repr_()
|
|
+ 'simple'
|
|
+ """
|
|
+ return 'simple'
|
|
+
|
|
+ def supported_output(self):
|
|
+ """
|
|
+ Return the outputs that are supported by the backend.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Iterable of output container classes, that is, subclass of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputBase`).
|
|
+ This backend only supports the plain text output container.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_base import BackendSimple
|
|
+ sage: backend = BackendSimple()
|
|
+ sage: backend.supported_output()
|
|
+ {<class 'sage.repl.rich_output.output_basic.OutputPlainText'>}
|
|
+ """
|
|
+ from sage.repl.rich_output.output_basic import OutputPlainText
|
|
+ return set([OutputPlainText])
|
|
+
|
|
+ def display_immediately(self, plain_text, rich_output):
|
|
+ """
|
|
+ Show output without going back to the command line prompt.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ Same as :meth:`~BackendBase.displayhook`.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ This backend returns nothing, it just prints to stdout.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_basic import OutputPlainText
|
|
+ sage: plain_text = OutputPlainText.example()
|
|
+ sage: from sage.repl.rich_output.backend_base import BackendSimple
|
|
+ sage: backend = BackendSimple()
|
|
+ sage: backend.display_immediately(plain_text, plain_text)
|
|
+ Example plain text output
|
|
+ """
|
|
+ print(rich_output.text.get())
|
|
+
|
|
diff --git a/src/sage/repl/rich_output/backend_doctest.py b/src/sage/repl/rich_output/backend_doctest.py
|
|
new file mode 100644
|
|
index 0000000..3689a7f
|
|
--- /dev/null
|
|
+++ b/src/sage/repl/rich_output/backend_doctest.py
|
|
@@ -0,0 +1,250 @@
|
|
+# -*- encoding: utf-8 -*-
|
|
+"""
|
|
+The backend used for doctests
|
|
+
|
|
+This backend is active during doctests. It should mimic the behavior
|
|
+of the IPython command line as close as possible. Without actually
|
|
+launching image viewers, of course.
|
|
+
|
|
+EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: get_display_manager()
|
|
+ The Sage display manager using the doctest backend
|
|
+"""
|
|
+
|
|
+#*****************************************************************************
|
|
+# Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com>
|
|
+#
|
|
+# Distributed under the terms of the GNU General Public License (GPL)
|
|
+# as published by the Free Software Foundation; either version 2 of
|
|
+# the License, or (at your option) any later version.
|
|
+# http://www.gnu.org/licenses/
|
|
+#*****************************************************************************
|
|
+
|
|
+import sys
|
|
+from sage.repl.rich_output.backend_base import BackendBase
|
|
+from sage.repl.rich_output.output_catalog import *
|
|
+
|
|
+
|
|
+
|
|
+class BackendDoctest(BackendBase):
|
|
+
|
|
+ def _repr_(self):
|
|
+ """
|
|
+ Return a string representation.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_doctest import BackendDoctest
|
|
+ sage: backend = BackendDoctest()
|
|
+ sage: backend._repr_()
|
|
+ 'doctest'
|
|
+ """
|
|
+ return 'doctest'
|
|
+
|
|
+ def install(self, **kwds):
|
|
+ """
|
|
+ Switch to the the doctest backend
|
|
+
|
|
+ This method is being called from within
|
|
+ :meth:`~sage.repl.rich_output.display_manager.DisplayManager.switch_backend`. You
|
|
+ should never call it by hand.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ None of the optional keyword arguments are used in the doctest
|
|
+ backend.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_doctest import BackendDoctest
|
|
+ sage: backend = BackendDoctest()
|
|
+ sage: backend.install()
|
|
+ sage: backend.uninstall()
|
|
+ """
|
|
+ self._old_displayhook = sys.displayhook
|
|
+ sys.displayhook = self.get_display_manager().displayhook
|
|
+
|
|
+ def uninstall(self):
|
|
+ """
|
|
+ Switch away from the doctest backend
|
|
+
|
|
+ This method is being called from within
|
|
+ :meth:`~sage.repl.rich_output.display_manager.DisplayManager.switch_backend`. You
|
|
+ should never call it by hand.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_doctest import BackendDoctest
|
|
+ sage: backend = BackendDoctest()
|
|
+ sage: backend.install()
|
|
+ sage: backend.uninstall()
|
|
+ """
|
|
+ sys.displayhook = self._old_displayhook
|
|
+
|
|
+ def supported_output(self):
|
|
+ """
|
|
+ Return the supported output types
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Set of subclasses of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputBase`, the
|
|
+ supported output container types.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_doctest import BackendDoctest
|
|
+ sage: from sage.repl.rich_output.output_catalog import *
|
|
+ sage: backend = BackendDoctest()
|
|
+ sage: OutputPlainText in backend.supported_output()
|
|
+ True
|
|
+ sage: OutputSceneJmol in backend.supported_output()
|
|
+ True
|
|
+ """
|
|
+ return set([
|
|
+ OutputPlainText, OutputAsciiArt, OutputLatex,
|
|
+ OutputImagePng, OutputImageGif, OutputImageJpg,
|
|
+ OutputImageSvg, OutputImagePdf, OutputImageDvi,
|
|
+ OutputSceneJmol, OutputSceneCanvas3d, OutputSceneWavefront,
|
|
+ ])
|
|
+
|
|
+ def displayhook(self, plain_text, rich_output):
|
|
+ """
|
|
+ Display object from displayhook
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``plain_text`` -- instance of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputPlainText`. The
|
|
+ plain text version of the output.
|
|
+
|
|
+ - ``rich_output`` -- instance of an output container class
|
|
+ (subclass of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputBase`). Guaranteed
|
|
+ to be one of the output containers returned from
|
|
+ :meth:`supported_output`, possibly the same as
|
|
+ ``plain_text``.
|
|
+
|
|
+ EXAMPLES:
|
|
+
|
|
+ This ends up calling the displayhook::
|
|
+
|
|
+ sage: plt = plot(sin)
|
|
+ sage: plt
|
|
+ Graphics object consisting of 1 graphics primitive
|
|
+ sage: plt.show()
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: dm.displayhook(plt) # indirect doctest
|
|
+ Graphics object consisting of 1 graphics primitive
|
|
+ """
|
|
+ self.validate(rich_output)
|
|
+ if any(isinstance(rich_output, cls) for cls in
|
|
+ [OutputPlainText, OutputAsciiArt, OutputLatex]):
|
|
+ rich_output.print_to_stdout()
|
|
+ else:
|
|
+ plain_text.print_to_stdout()
|
|
+
|
|
+ def display_immediately(self, plain_text, rich_output):
|
|
+ """
|
|
+ Display object immediately
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ Same as :meth:`displayhook`.
|
|
+
|
|
+ EXAMPLES:
|
|
+
|
|
+ The following example does not call the displayhook. More
|
|
+ precisely, the ``show()`` method returns ``None`` which is
|
|
+ ignored by the displayhook. When running the example on a Sage
|
|
+ display backend capable of displaying graphics outside of the
|
|
+ displayhook, the plot is still shown. Nothing is shown during
|
|
+ doctests::
|
|
+
|
|
+ sage: plt = plot(sin)
|
|
+ sage: plt
|
|
+ Graphics object consisting of 1 graphics primitive
|
|
+ sage: plt.show()
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: dm.display_immediately(plt) # indirect doctest
|
|
+ """
|
|
+ self.validate(rich_output)
|
|
+ if any(isinstance(rich_output, cls) for cls in
|
|
+ [OutputPlainText, OutputAsciiArt, OutputLatex]):
|
|
+ rich_output.print_to_stdout()
|
|
+
|
|
+ def validate(self, rich_output):
|
|
+ """
|
|
+ Perform checks on ``rich_output``
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``rich_output`` -- instance of a subclass of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputBase`.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ An assertion is triggered if ``rich_output`` is invalid.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: invalid = dm.types.OutputImagePng('invalid')
|
|
+ sage: backend = dm._backend; backend
|
|
+ doctest
|
|
+ sage: backend.validate(invalid)
|
|
+ Traceback (most recent call last):
|
|
+ ...
|
|
+ AssertionError
|
|
+ sage: backend.validate(dm.types.OutputPlainText.example())
|
|
+ sage: backend.validate(dm.types.OutputAsciiArt.example())
|
|
+ sage: backend.validate(dm.types.OutputLatex.example())
|
|
+ sage: backend.validate(dm.types.OutputImagePng.example())
|
|
+ sage: backend.validate(dm.types.OutputImageGif.example())
|
|
+ sage: backend.validate(dm.types.OutputImageJpg.example())
|
|
+ sage: backend.validate(dm.types.OutputImageSvg.example())
|
|
+ sage: backend.validate(dm.types.OutputImagePdf.example())
|
|
+ sage: backend.validate(dm.types.OutputImageDvi.example())
|
|
+ sage: backend.validate(dm.types.OutputSceneJmol.example())
|
|
+ sage: backend.validate(dm.types.OutputSceneWavefront.example())
|
|
+ sage: backend.validate(dm.types.OutputSceneCanvas3d.example())
|
|
+ """
|
|
+ if isinstance(rich_output, OutputPlainText):
|
|
+ pass
|
|
+ elif isinstance(rich_output, OutputAsciiArt):
|
|
+ pass
|
|
+ elif isinstance(rich_output, OutputLatex):
|
|
+ assert rich_output.mathjax().startswith('<html>')
|
|
+ elif isinstance(rich_output, OutputImagePng):
|
|
+ assert rich_output.png.get().startswith('\x89PNG')
|
|
+ elif isinstance(rich_output, OutputImageGif):
|
|
+ assert rich_output.gif.get().startswith('GIF89a')
|
|
+ elif isinstance(rich_output, OutputImageJpg):
|
|
+ assert rich_output.jpg.get().startswith('\xff\xd8\xff\xe0\x00\x10JFIF')
|
|
+ elif isinstance(rich_output, OutputImageSvg):
|
|
+ assert '</svg>' in rich_output.svg.get()
|
|
+ elif isinstance(rich_output, OutputImagePdf):
|
|
+ assert rich_output.pdf.get().startswith('%PDF-')
|
|
+ elif isinstance(rich_output, OutputImageDvi):
|
|
+ assert 'TeX output' in rich_output.dvi.get()
|
|
+ elif isinstance(rich_output, OutputSceneJmol):
|
|
+ assert rich_output.preview_png.get().startswith('\x89PNG')
|
|
+ assert rich_output.scene_zip.get().startswith('PK') # zip archive
|
|
+ elif isinstance(rich_output, OutputSceneWavefront):
|
|
+ assert rich_output.obj.get().startswith('mtllib ')
|
|
+ assert rich_output.mtl.get().startswith('newmtl ')
|
|
+ elif isinstance(rich_output, OutputSceneCanvas3d):
|
|
+ assert rich_output.canvas3d.get().startswith('[{vertices:')
|
|
+ else:
|
|
+ raise TypeError('rich_output type not supported')
|
|
diff --git a/src/sage/repl/rich_output/backend_ipython.py b/src/sage/repl/rich_output/backend_ipython.py
|
|
new file mode 100644
|
|
index 0000000..e0889f0
|
|
--- /dev/null
|
|
+++ b/src/sage/repl/rich_output/backend_ipython.py
|
|
@@ -0,0 +1,487 @@
|
|
+# -*- encoding: utf-8 -*-
|
|
+"""
|
|
+IPython Backend for the Sage Rich Output System
|
|
+
|
|
+This module defines the IPython backends for
|
|
+:mod:`sage.repl.rich_output`.
|
|
+"""
|
|
+
|
|
+#*****************************************************************************
|
|
+# Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com>
|
|
+#
|
|
+# Distributed under the terms of the GNU General Public License (GPL)
|
|
+# as published by the Free Software Foundation; either version 2 of
|
|
+# the License, or (at your option) any later version.
|
|
+# http://www.gnu.org/licenses/
|
|
+#*****************************************************************************
|
|
+
|
|
+import os
|
|
+from sage.repl.rich_output.backend_base import BackendBase
|
|
+from sage.repl.rich_output.output_catalog import *
|
|
+
|
|
+
|
|
+class BackendIPython(BackendBase):
|
|
+ """
|
|
+ Common base for the IPython UIs
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_ipython import BackendIPython
|
|
+ sage: BackendIPython()._repr_()
|
|
+ Traceback (most recent call last):
|
|
+ NotImplementedError: derived classes must implement this method
|
|
+ """
|
|
+
|
|
+ def install(self, **kwds):
|
|
+ """
|
|
+ Switch the Sage rich output to the IPython backend
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``shell`` -- keyword argument. The IPython shell.
|
|
+
|
|
+ No tests since switching away from the doctest rich output
|
|
+ backend will break the doctests.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.interpreter import get_test_shell
|
|
+ sage: from sage.repl.rich_output.backend_ipython import BackendIPython
|
|
+ sage: backend = BackendIPython()
|
|
+ sage: shell = get_test_shell();
|
|
+ sage: backend.install(shell=shell)
|
|
+ sage: shell.run_cell('1+1')
|
|
+ 2
|
|
+ """
|
|
+ shell = kwds['shell']
|
|
+ from sage.repl.display.formatter import SageDisplayFormatter
|
|
+ shell.display_formatter = SageDisplayFormatter(parent=shell)
|
|
+ shell.configurables.append(shell.display_formatter)
|
|
+
|
|
+ def set_underscore_variable(self, obj):
|
|
+ """
|
|
+ Set the ``_`` builtin variable.
|
|
+
|
|
+ Since IPython handles the history itself, this does nothing.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``obj`` -- anything.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.interpreter import get_test_shell
|
|
+ sage: from sage.repl.rich_output.backend_ipython import BackendIPython
|
|
+ sage: backend = BackendIPython()
|
|
+ sage: backend.set_underscore_variable(123)
|
|
+ sage: _
|
|
+ 0
|
|
+ """
|
|
+ pass
|
|
+
|
|
+
|
|
+class BackendIPythonCommandline(BackendIPython):
|
|
+ """
|
|
+ Backend for the IPython Command Line
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline
|
|
+ sage: BackendIPythonCommandline()
|
|
+ IPython command line
|
|
+ """
|
|
+
|
|
+ def _repr_(self):
|
|
+ """
|
|
+ Return a string representation
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline
|
|
+ sage: backend = BackendIPythonCommandline()
|
|
+ sage: backend._repr_()
|
|
+ 'IPython command line'
|
|
+ """
|
|
+ return 'IPython command line'
|
|
+
|
|
+ def supported_output(self):
|
|
+ """
|
|
+ Return the outputs that are supported by the IPython commandline backend.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Iterable of output container classes, that is, subclass of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputBase`).
|
|
+ The order is ignored.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline
|
|
+ sage: backend = BackendIPythonCommandline()
|
|
+ sage: supp = backend.supported_output(); supp # random output
|
|
+ set([<class 'sage.repl.rich_output.output_graphics.OutputImageGif'>,
|
|
+ ...,
|
|
+ <class 'sage.repl.rich_output.output_graphics.OutputImagePng'>])
|
|
+ sage: from sage.repl.rich_output.output_basic import OutputLatex
|
|
+ sage: OutputLatex in supp
|
|
+ True
|
|
+ """
|
|
+ return set([
|
|
+ OutputPlainText, OutputAsciiArt, OutputLatex,
|
|
+ OutputImagePng, OutputImageGif,
|
|
+ OutputImagePdf, OutputImageDvi,
|
|
+ OutputSceneJmol, OutputSceneWavefront,
|
|
+ ])
|
|
+
|
|
+ def displayhook(self, plain_text, rich_output):
|
|
+ """
|
|
+ Backend implementation of the displayhook
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``plain_text`` -- instance of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputPlainText`. The
|
|
+ plain text version of the output.
|
|
+
|
|
+ - ``rich_output`` -- instance of an output container class
|
|
+ (subclass of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputBase`). Guaranteed
|
|
+ to be one of the output containers returned from
|
|
+ :meth:`supported_output`, possibly the same as
|
|
+ ``plain_text``.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ The IPython commandline display hook returns the IPython
|
|
+ display data, a pair of dictionaries. The first dictionary
|
|
+ contains mime types as keys and the respective output as
|
|
+ value. The second dictionary is metadata.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_basic import OutputPlainText
|
|
+ sage: plain_text = OutputPlainText.example()
|
|
+ sage: from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline
|
|
+ sage: backend = BackendIPythonCommandline()
|
|
+ sage: backend.displayhook(plain_text, plain_text)
|
|
+ ({u'text/plain': 'Example plain text output'}, {})
|
|
+ """
|
|
+ if isinstance(rich_output, OutputPlainText):
|
|
+ return ({u'text/plain': rich_output.text.get()}, {})
|
|
+ elif isinstance(rich_output, OutputAsciiArt):
|
|
+ return ({u'text/plain': rich_output.ascii_art.get()}, {})
|
|
+ elif isinstance(rich_output, OutputLatex):
|
|
+ return ({u'text/plain': rich_output.latex.get()}, {})
|
|
+ elif isinstance(rich_output, OutputImagePng):
|
|
+ msg = self.launch_viewer(
|
|
+ rich_output.png.filename(ext='png'), plain_text.text.get())
|
|
+ return ({u'text/plain': msg}, {})
|
|
+ elif isinstance(rich_output, OutputImageGif):
|
|
+ msg = self.launch_viewer(
|
|
+ rich_output.gif.filename(ext='gif'), plain_text.text.get())
|
|
+ return ({u'text/plain': msg}, {})
|
|
+ elif isinstance(rich_output, OutputImagePdf):
|
|
+ msg = self.launch_viewer(
|
|
+ rich_output.pdf.filename(ext='pdf'), plain_text.text.get())
|
|
+ return ({u'text/plain': msg}, {})
|
|
+ elif isinstance(rich_output, OutputImageDvi):
|
|
+ msg = self.launch_viewer(
|
|
+ rich_output.dvi.filename(ext='dvi'), plain_text.text.get())
|
|
+ return ({u'text/plain': msg}, {})
|
|
+ elif isinstance(rich_output, OutputSceneJmol):
|
|
+ msg = self.launch_jmol(rich_output, plain_text.text.get())
|
|
+ return ({u'text/plain': msg}, {})
|
|
+ elif isinstance(rich_output, OutputSceneWavefront):
|
|
+ msg = self.launch_sage3d(rich_output, plain_text.text.get())
|
|
+ return ({u'text/plain': msg}, {})
|
|
+ else:
|
|
+ raise TypeError('rich_output type not supported')
|
|
+
|
|
+ def display_immediately(self, plain_text, rich_output):
|
|
+ """
|
|
+ Show output without going back to the command line prompt.
|
|
+
|
|
+ This method is similar to the rich output :meth:`displayhook`,
|
|
+ except that it can be invoked at any time. On the Sage command
|
|
+ line it launches viewers just like :meth:`displayhook`.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ Same as :meth:`displayhook`.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ This method does not return anything.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_basic import OutputPlainText
|
|
+ sage: plain_text = OutputPlainText.example()
|
|
+ sage: from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline
|
|
+ sage: backend = BackendIPythonCommandline()
|
|
+ sage: backend.display_immediately(plain_text, plain_text)
|
|
+ Example plain text output
|
|
+ """
|
|
+ formatdata, metadata = self.displayhook(plain_text, rich_output)
|
|
+ print(formatdata[u'text/plain'])
|
|
+
|
|
+ def launch_viewer(self, image_file, plain_text):
|
|
+ """
|
|
+ Launch external viewer for the graphics file.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``image_file`` -- string. File name of the image file.
|
|
+
|
|
+ - ``plain_text`` -- string. The plain text representation of
|
|
+ the image file.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String. Human-readable message indicating whether the viewer
|
|
+ was launched successfully.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline
|
|
+ sage: backend = BackendIPythonCommandline()
|
|
+ sage: backend.launch_viewer('/path/to/foo.bar', 'Graphics object')
|
|
+ 'Launched bar viewer for Graphics object'
|
|
+ """
|
|
+ base, dot_ext = os.path.splitext(image_file)
|
|
+ ext = dot_ext.lstrip(os.path.extsep)
|
|
+ from sage.misc.viewer import viewer
|
|
+ command = viewer(ext)
|
|
+ if not command:
|
|
+ command = viewer.browser()
|
|
+ from sage.doctest import DOCTEST_MODE
|
|
+ if not DOCTEST_MODE:
|
|
+ os.system('{0} {1} 2>/dev/null 1>/dev/null &'
|
|
+ .format(command, image_file))
|
|
+ return 'Launched {0} viewer for {1}'.format(ext, plain_text)
|
|
+
|
|
+ def launch_jmol(self, output_jmol, plain_text):
|
|
+ """
|
|
+ Launch the stand-alone jmol viewer
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``output_jmol`` --
|
|
+ :class:`~sage.repl.rich_output.output_graphics3d.OutputSceneJmol`. The
|
|
+ scene to launch Jmol with.
|
|
+
|
|
+ - ``plain_text`` -- string. The plain text representation.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String. Human-readable message indicating that the viewer was launched.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline
|
|
+ sage: backend = BackendIPythonCommandline()
|
|
+ sage: from sage.repl.rich_output.output_graphics3d import OutputSceneJmol
|
|
+ sage: backend.launch_jmol(OutputSceneJmol.example(), 'Graphics3d object')
|
|
+ 'Launched jmol viewer for Graphics3d object'
|
|
+ """
|
|
+ from sage.doctest import DOCTEST_MODE
|
|
+ from sage.interfaces.jmoldata import JmolData
|
|
+ jdata = JmolData()
|
|
+ if not jdata.is_jvm_available() and not DOCTEST_MODE:
|
|
+ raise RuntimeError('jmol cannot run, no suitable java version found')
|
|
+ launch_script = output_jmol.launch_script_filename()
|
|
+ from sage.env import SAGE_LOCAL
|
|
+ jmol_cmd = os.path.join(SAGE_LOCAL, 'bin', 'jmol')
|
|
+ if not DOCTEST_MODE:
|
|
+ os.system('{0} {1} 2>/dev/null 1>/dev/null &'
|
|
+ .format(jmol_cmd, launch_script))
|
|
+ return 'Launched jmol viewer for {0}'.format(plain_text)
|
|
+
|
|
+ def launch_sage3d(self, output_wavefront, plain_text):
|
|
+ """
|
|
+ Launch the stand-alone java3d viewer
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``output_wavefront`` --
|
|
+ :class:`~sage.repl.rich_output.output_graphics3d.OutputSceneWavefront`. The
|
|
+ scene to launch Java3d with.
|
|
+
|
|
+ - ``plain_text`` -- string. The plain text representation.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String. Human-readable message indicating that the viewer was launched.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline
|
|
+ sage: backend = BackendIPythonCommandline()
|
|
+ sage: from sage.repl.rich_output.output_graphics3d import OutputSceneWavefront
|
|
+ sage: backend.launch_sage3d(OutputSceneWavefront.example(), 'Graphics3d object')
|
|
+ 'Launched Java 3D viewer for Graphics3d object'
|
|
+ """
|
|
+ from sage.env import SAGE_LOCAL
|
|
+ sage3d = os.path.join(SAGE_LOCAL, 'bin', 'sage3d')
|
|
+ obj = output_wavefront.obj_filename()
|
|
+ from sage.doctest import DOCTEST_MODE
|
|
+ if not DOCTEST_MODE:
|
|
+ os.system('{0} {1} 2>/dev/null 1>/dev/null &'
|
|
+ .format(sage3d, obj))
|
|
+ return 'Launched Java 3D viewer for {0}'.format(plain_text)
|
|
+
|
|
+
|
|
+class BackendIPythonNotebook(BackendIPython):
|
|
+ """
|
|
+ Backend for the IPython Notebook
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_ipython import BackendIPythonNotebook
|
|
+ sage: BackendIPythonNotebook()
|
|
+ IPython notebook
|
|
+ """
|
|
+
|
|
+ def _repr_(self):
|
|
+ """
|
|
+ Return string representation of the backend
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_ipython import BackendIPythonNotebook
|
|
+ sage: backend = BackendIPythonNotebook()
|
|
+ sage: backend._repr_()
|
|
+ 'IPython notebook'
|
|
+ """
|
|
+ return 'IPython notebook'
|
|
+
|
|
+ def supported_output(self):
|
|
+ """
|
|
+ Return the outputs that are supported by the IPython notebook backend.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Iterable of output container classes, that is, subclass of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputBase`).
|
|
+ The order is ignored.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_ipython import BackendIPythonNotebook
|
|
+ sage: backend = BackendIPythonNotebook()
|
|
+ sage: supp = backend.supported_output(); supp # random output
|
|
+ set([<class 'sage.repl.rich_output.output_graphics.OutputPlainText'>,
|
|
+ ...,
|
|
+ <class 'sage.repl.rich_output.output_graphics.OutputImagePdf'>])
|
|
+ sage: from sage.repl.rich_output.output_basic import OutputLatex
|
|
+ sage: OutputLatex in supp
|
|
+ True
|
|
+
|
|
+ The IPython notebook cannot display gif images, see
|
|
+ https://github.com/ipython/ipython/issues/2115 ::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_graphics import OutputImageGif
|
|
+ sage: OutputImageGif in supp
|
|
+ False
|
|
+ """
|
|
+ return set([
|
|
+ OutputPlainText, OutputAsciiArt, OutputLatex,
|
|
+ OutputImagePng, OutputImageJpg,
|
|
+ OutputImageSvg, OutputImagePdf,
|
|
+ ])
|
|
+
|
|
+ def displayhook(self, plain_text, rich_output):
|
|
+ """
|
|
+ Backend implementation of the displayhook
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``plain_text`` -- instance of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputPlainText`. The
|
|
+ plain text version of the output.
|
|
+
|
|
+ - ``rich_output`` -- instance of an output container class
|
|
+ (subclass of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputBase`). Guaranteed
|
|
+ to be one of the output containers returned from
|
|
+ :meth:`supported_output`, possibly the same as
|
|
+ ``plain_text``.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ The IPython notebook display hook returns the IPython
|
|
+ display data, a pair of dictionaries. The first dictionary
|
|
+ contains mime types as keys and the respective output as
|
|
+ value. The second dictionary is metadata.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_basic import OutputPlainText
|
|
+ sage: plain_text = OutputPlainText.example()
|
|
+ sage: from sage.repl.rich_output.backend_ipython import BackendIPythonNotebook
|
|
+ sage: backend = BackendIPythonNotebook()
|
|
+ sage: backend.displayhook(plain_text, plain_text)
|
|
+ ({u'text/plain': 'Example plain text output'}, {})
|
|
+ """
|
|
+ if isinstance(rich_output, OutputPlainText):
|
|
+ return ({u'text/plain': rich_output.text.get()}, {})
|
|
+ elif isinstance(rich_output, OutputAsciiArt):
|
|
+ return ({u'text/plain': rich_output.ascii_art.get()}, {})
|
|
+ elif isinstance(rich_output, OutputLatex):
|
|
+ return ({u'text/html': rich_output.mathjax(),
|
|
+ u'text/plain': plain_text.text.get(),
|
|
+ }, {})
|
|
+ elif isinstance(rich_output, OutputImagePng):
|
|
+ return ({u'image/png': rich_output.png.get(),
|
|
+ u'text/plain': plain_text.text.get(),
|
|
+ }, {})
|
|
+ elif isinstance(rich_output, OutputImageJpg):
|
|
+ return ({u'image/jpeg': rich_output.jpg.get(),
|
|
+ u'text/plain': plain_text.text.get(),
|
|
+ }, {})
|
|
+
|
|
+ elif isinstance(rich_output, OutputImageSvg):
|
|
+ return ({u'image/svg+xml': rich_output.svg.get(),
|
|
+ u'text/plain': plain_text.text.get(),
|
|
+ }, {})
|
|
+ elif isinstance(rich_output, OutputImagePdf):
|
|
+ return ({u'image/png': rich_output.png.get(),
|
|
+ u'text/plain': plain_text.text.get(),
|
|
+ }, {})
|
|
+ else:
|
|
+ raise TypeError('rich_output type not supported')
|
|
+
|
|
+
|
|
+ def display_immediately(self, plain_text, rich_output):
|
|
+ """
|
|
+ Show output immediately.
|
|
+
|
|
+ This method is similar to the rich output :meth:`displayhook`,
|
|
+ except that it can be invoked at any time.
|
|
+
|
|
+ .. TODO::
|
|
+
|
|
+ This does not work currently.
|
|
+
|
|
+ INPUT/OUTPUT:
|
|
+
|
|
+ Same as :meth:`displayhook`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_basic import OutputPlainText
|
|
+ sage: plain_text = OutputPlainText.example()
|
|
+ sage: from sage.repl.rich_output.backend_ipython import BackendIPythonNotebook
|
|
+ sage: backend = BackendIPythonNotebook()
|
|
+ sage: backend.display_immediately(plain_text, plain_text)
|
|
+ ({u'text/plain': 'Example plain text output'}, {})
|
|
+ """
|
|
+ return self.displayhook(plain_text, rich_output)
|
|
diff --git a/src/sage/repl/rich_output/backend_sagenb.py b/src/sage/repl/rich_output/backend_sagenb.py
|
|
new file mode 100644
|
|
index 0000000..dc860e3
|
|
--- /dev/null
|
|
+++ b/src/sage/repl/rich_output/backend_sagenb.py
|
|
@@ -0,0 +1,403 @@
|
|
+# -*- encoding: utf-8 -*-
|
|
+"""
|
|
+SageNB Backend for the Sage Rich Output System
|
|
+
|
|
+This module defines the IPython backends for
|
|
+:mod:`sage.repl.rich_output`.
|
|
+
|
|
+EXAMPLES:
|
|
+
|
|
+Install the SageNB displayhook while doctesting for the rest of this
|
|
+file. This somewhat odd incantation is how SageNB installed its
|
|
+displayhook before::
|
|
+
|
|
+ sage: from sage.misc.displayhook import DisplayHook
|
|
+ sage: import sys
|
|
+ sage: sys.displayhook = DisplayHook()
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: get_display_manager()
|
|
+ The Sage display manager using the SageNB backend
|
|
+
|
|
+We also enable the SageNB magic global variable::
|
|
+
|
|
+ sage: import sage.plot.plot
|
|
+ sage: sage.plot.plot.EMBEDDED_MODE = True
|
|
+
|
|
+And switch to a temporary directory so our current directory will not
|
|
+get cluttered with temporary files::
|
|
+
|
|
+ sage: os.chdir(tmp_dir())
|
|
+
|
|
+The SageNB notebook is based on saving data files with predictable
|
|
+filenames::
|
|
+
|
|
+ sage: os.path.exists('sage0.png')
|
|
+ False
|
|
+ sage: Graphics()
|
|
+ sage: os.path.exists('sage0.png')
|
|
+ True
|
|
+ sage: os.remove('sage0.png')
|
|
+"""
|
|
+
|
|
+#*****************************************************************************
|
|
+# Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com>
|
|
+#
|
|
+# Distributed under the terms of the GNU General Public License (GPL)
|
|
+# as published by the Free Software Foundation; either version 2 of
|
|
+# the License, or (at your option) any later version.
|
|
+# http://www.gnu.org/licenses/
|
|
+#*****************************************************************************
|
|
+
|
|
+import os
|
|
+import stat
|
|
+from sage.misc.cachefunc import cached_method
|
|
+from sage.misc.temporary_file import graphics_filename
|
|
+from sage.doctest import DOCTEST_MODE
|
|
+from sage.repl.rich_output.backend_base import BackendBase
|
|
+from sage.repl.rich_output.output_catalog import *
|
|
+
|
|
+
|
|
+def world_readable(filename):
|
|
+ """
|
|
+ All SageNB temporary files must be world-writeable.
|
|
+
|
|
+ Discussion of this design choice can be found at :trac:`17743`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: import os, stat
|
|
+ sage: f = tmp_filename()
|
|
+
|
|
+ At least on a sane system the temporary files are only readable by
|
|
+ the user, but not by others in the group or total strangers::
|
|
+
|
|
+ sage: mode = os.stat(f).st_mode
|
|
+ sage: bool(mode & stat.S_IRUSR), bool(mode & stat.S_IRGRP), bool(mode & stat.S_IROTH) # random output
|
|
+ (True, False, False)
|
|
+
|
|
+ This function disables that protection::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_sagenb import world_readable
|
|
+ sage: world_readable(f)
|
|
+ sage: mode = os.stat(f).st_mode
|
|
+ sage: bool(mode & stat.S_IRUSR), bool(mode & stat.S_IRGRP), bool(mode & stat.S_IROTH)
|
|
+ (True, True, True)
|
|
+ """
|
|
+ os.chmod(filename, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
|
|
+
|
|
+
|
|
+class SageNbOutputSceneJmol(OutputSceneJmol):
|
|
+ """
|
|
+ Adapt Jmol rich output container for SageNB.
|
|
+
|
|
+ For legacy reasons, SageNB expects Jmol files saved under strange
|
|
+ names. This class takes care of that.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol
|
|
+ sage: SageNbOutputSceneJmol.example()
|
|
+ SageNbOutputSceneJmol container
|
|
+ """
|
|
+
|
|
+ @cached_method
|
|
+ def sagenb_launch_script_filename(self):
|
|
+ """
|
|
+ Return the launch script filename used by SageNB
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol
|
|
+ sage: j = SageNbOutputSceneJmol.example()
|
|
+ sage: j.sagenb_launch_script_filename()
|
|
+ 'sage0-size32.jmol'
|
|
+ """
|
|
+ import PIL.Image
|
|
+ from StringIO import StringIO
|
|
+ width, height = PIL.Image.open(StringIO(self.preview_png.get())).size
|
|
+ ext = '-size{0}.jmol'.format(width)
|
|
+ return graphics_filename(ext=ext)
|
|
+
|
|
+ @cached_method
|
|
+ def _base_filename(self):
|
|
+ """
|
|
+ Return the common part of the file name
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol
|
|
+ sage: j = SageNbOutputSceneJmol.example()
|
|
+ sage: j._base_filename()
|
|
+ 'sage0-size32'
|
|
+ sage: j.sagenb_launch_script_filename().startswith(j._base_filename())
|
|
+ True
|
|
+ sage: j.scene_zip_filename().startswith(j._base_filename())
|
|
+ True
|
|
+ """
|
|
+ dot_jmol = '.jmol'
|
|
+ filename = self.sagenb_launch_script_filename()
|
|
+ assert filename.endswith(dot_jmol)
|
|
+ return filename[:-len(dot_jmol)]
|
|
+
|
|
+ @cached_method
|
|
+ def scene_zip_filename(self):
|
|
+ """
|
|
+ Return the filename for the scene zip archive
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol
|
|
+ sage: j = SageNbOutputSceneJmol.example()
|
|
+ sage: j.scene_zip_filename()
|
|
+ 'sage0-size32-....jmol.zip'
|
|
+ """
|
|
+ from random import randint
|
|
+ return '{0}-{1}.jmol.zip'.format(
|
|
+ self._base_filename(),
|
|
+ randint(0, 1 << 30)
|
|
+ )
|
|
+
|
|
+ @cached_method
|
|
+ def preview_filename(self):
|
|
+ """
|
|
+ Return the filename for the png preview
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol
|
|
+ sage: j = SageNbOutputSceneJmol.example()
|
|
+ sage: j.preview_filename()
|
|
+ './.jmol_images/sage0-size32.jmol.png'
|
|
+ """
|
|
+ directory, filename = os.path.split(self._base_filename())
|
|
+ if not directory:
|
|
+ directory = '.'
|
|
+ return '{0}/.jmol_images/{1}.jmol.png'.format(directory, filename)
|
|
+
|
|
+ def save_launch_script(self):
|
|
+ """
|
|
+ Save the Jmol launch script
|
|
+
|
|
+ See :meth:`sagenb_launch_script_filename`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol
|
|
+ sage: j = SageNbOutputSceneJmol.example()
|
|
+ sage: os.path.exists('sage0-size32.jmol')
|
|
+ False
|
|
+ sage: j.save_launch_script()
|
|
+ sage: os.path.exists('sage0-size32.jmol')
|
|
+ True
|
|
+ """
|
|
+ from sagenb.notebook.interact import SAGE_CELL_ID
|
|
+ path = 'cells/{0}/{1}'.format(
|
|
+ SAGE_CELL_ID,
|
|
+ self.scene_zip_filename())
|
|
+ with open(self.sagenb_launch_script_filename(), 'w') as f:
|
|
+ f.write('set defaultdirectory "{0}"\n'.format(path))
|
|
+ f.write('script SCRIPT\n')
|
|
+ world_readable(self.sagenb_launch_script_filename())
|
|
+
|
|
+ def save_preview(self):
|
|
+ """
|
|
+ Save the preview PNG image
|
|
+
|
|
+ See :meth:`preview_filename`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol
|
|
+ sage: j = SageNbOutputSceneJmol.example()
|
|
+ sage: import shutil
|
|
+ sage: shutil.rmtree('.jmol_images', ignore_errors=True)
|
|
+ sage: j.save_preview()
|
|
+ sage: os.listdir('.jmol_images')
|
|
+ ['sage1-size32.jmol.png']
|
|
+ """
|
|
+ from sage.misc.misc import sage_makedirs
|
|
+ sage_makedirs('.jmol_images')
|
|
+ self.preview_png.save_as(self.preview_filename())
|
|
+ world_readable(self.preview_filename())
|
|
+
|
|
+ def embed(self):
|
|
+ """
|
|
+ Save all files necessary to embed jmol
|
|
+
|
|
+ EXAMPLES:
|
|
+
|
|
+ Switch to a new empty temporary directory::
|
|
+
|
|
+ sage: os.chdir(tmp_dir())
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol
|
|
+ sage: j = SageNbOutputSceneJmol.example()
|
|
+ sage: j.embed()
|
|
+ sage: sorted(os.listdir('.'))
|
|
+ ['.jmol_images', 'sage0-size32-....jmol.zip', 'sage0-size32.jmol']
|
|
+ sage: sorted(os.listdir('.jmol_images'))
|
|
+ ['sage0-size32.jmol.png']
|
|
+ """
|
|
+ self.save_preview()
|
|
+ self.save_launch_script()
|
|
+ self.scene_zip.save_as(self.scene_zip_filename())
|
|
+ world_readable(self.scene_zip_filename())
|
|
+
|
|
+
|
|
+class BackendSageNB(BackendBase):
|
|
+
|
|
+ def _repr_(self):
|
|
+ """
|
|
+ Return the string representation
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_sagenb import BackendSageNB
|
|
+ sage: backend = BackendSageNB()
|
|
+ sage: backend._repr_()
|
|
+ 'SageNB'
|
|
+ """
|
|
+ return 'SageNB'
|
|
+
|
|
+ def supported_output(self):
|
|
+ """
|
|
+ Return the outputs that are supported by the SageNB backend.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Iterable of output container classes, that is, subclass of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputBase`).
|
|
+ The order is ignored.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_sagenb import BackendSageNB
|
|
+ sage: backend = BackendSageNB()
|
|
+ sage: supp = backend.supported_output(); supp # random output
|
|
+ set([<class 'sage.repl.rich_output.output_graphics.OutputPlainText'>,
|
|
+ ...,
|
|
+ <class 'sage.repl.rich_output.output_graphics.OutputCanvas3d'>])
|
|
+ sage: from sage.repl.rich_output.output_basic import OutputLatex
|
|
+ sage: OutputLatex in supp
|
|
+ True
|
|
+ """
|
|
+ return set([
|
|
+ OutputPlainText, OutputAsciiArt, OutputLatex,
|
|
+ OutputImagePng, OutputImageGif, OutputImageJpg,
|
|
+ OutputImagePdf, OutputImageSvg,
|
|
+ SageNbOutputSceneJmol,
|
|
+ OutputSceneCanvas3d,
|
|
+ ])
|
|
+
|
|
+ def display_immediately(self, plain_text, rich_output):
|
|
+ """
|
|
+ Show output without waiting for the prompt.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``plain_text`` -- instance of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputPlainText`. The
|
|
+ plain text version of the output.
|
|
+
|
|
+ - ``rich_output`` -- instance of an output container class
|
|
+ (subclass of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputBase`). Guaranteed
|
|
+ to be one of the output containers returned from
|
|
+ :meth:`supported_output`, possibly the same as
|
|
+ ``plain_text``.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ This method does not return anything.
|
|
+
|
|
+ EXAMPLES:
|
|
+
|
|
+ sage: import sage.repl.rich_output.output_catalog as catalog
|
|
+ sage: plain_text = catalog.OutputPlainText.example()
|
|
+ sage: from sage.repl.rich_output.backend_sagenb import BackendSageNB
|
|
+ sage: backend = BackendSageNB()
|
|
+ sage: backend.display_immediately(plain_text, plain_text)
|
|
+ Example plain text output
|
|
+ sage: latex = catalog.OutputLatex.example()
|
|
+ sage: backend.display_immediately(plain_text, latex)
|
|
+ <html><script type="math/tex; mode=display">\newcommand{\Bold}[1]{\mathbf{#1}}\int \sin\left(x\right)\,{d x}</script></html>
|
|
+ """
|
|
+ if isinstance(rich_output, (OutputPlainText, OutputAsciiArt)):
|
|
+ rich_output.print_to_stdout()
|
|
+ elif isinstance(rich_output, OutputLatex):
|
|
+ print(rich_output.mathjax())
|
|
+ elif isinstance(rich_output, OutputImagePng):
|
|
+ self.embed_image(rich_output.png, '.png')
|
|
+ elif isinstance(rich_output, OutputImageGif):
|
|
+ self.embed_image(rich_output.gif, '.gif')
|
|
+ elif isinstance(rich_output, OutputImageJpg):
|
|
+ self.embed_image(rich_output.jpg, '.jpg')
|
|
+ elif isinstance(rich_output, OutputImagePdf):
|
|
+ self.embed_image(rich_output.pdf, '.pdf')
|
|
+ elif isinstance(rich_output, OutputImageSvg):
|
|
+ self.embed_image(rich_output.svg, '.svg')
|
|
+ elif isinstance(rich_output, OutputSceneJmol):
|
|
+ rich_output.embed()
|
|
+ elif isinstance(rich_output, OutputSceneCanvas3d):
|
|
+ self.embed_image(rich_output.canvas3d, '.canvas3d')
|
|
+ else:
|
|
+ raise TypeError('rich_output type not supported, got {0}'.format(rich_output))
|
|
+
|
|
+ def embed_image(self, output_buffer, file_ext):
|
|
+ """
|
|
+ Embed Image in the SageNB worksheet
|
|
+
|
|
+ SageNB scans per-cell directories for image files, so all we
|
|
+ have to do here is to save the image at the right place.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``output_buffer`` --
|
|
+ :class:`~sage.repl.rich_output.buffer.Buffer`. A buffer
|
|
+ holding the image data.
|
|
+
|
|
+ - ``file_ext`` -- string. The file extension to use for saving
|
|
+ the image.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Nothing is returned. The image file is saved in the
|
|
+ appropriate place for SageNB.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: rich_output = dm.types.OutputImagePng.example()
|
|
+ sage: os.path.exists('sage0.png')
|
|
+ False
|
|
+ sage: dm._backend.embed_image(rich_output.png, '.png')
|
|
+ sage: os.path.exists('sage0.png')
|
|
+ True
|
|
+ """
|
|
+ filename = graphics_filename(ext=file_ext)
|
|
+ output_buffer.save_as(filename)
|
|
+ world_readable(filename)
|
|
+
|
|
+
|
|
diff --git a/src/sage/repl/rich_output/backend_test.py b/src/sage/repl/rich_output/backend_test.py
|
|
new file mode 100644
|
|
index 0000000..fcea172
|
|
--- /dev/null
|
|
+++ b/src/sage/repl/rich_output/backend_test.py
|
|
@@ -0,0 +1,204 @@
|
|
+# -*- encoding: utf-8 -*-
|
|
+r"""
|
|
+Test Backend
|
|
+
|
|
+This backend is only for doctesting purposes.
|
|
+
|
|
+EXAMPLES:
|
|
+
|
|
+We switch to the test backend for the remainder of this file::
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: from sage.repl.rich_output.backend_test import BackendTest, TestObject
|
|
+ sage: doctest_backend = dm.switch_backend(BackendTest())
|
|
+ sage: dm
|
|
+ The Sage display manager using the test backend
|
|
+
|
|
+ sage: dm._output_promotions
|
|
+ {<class 'sage.repl.rich_output.output_basic.OutputPlainText'>:
|
|
+ <class 'sage.repl.rich_output.backend_test.TestOutputPlainText'>}
|
|
+ sage: dm.displayhook(1/2)
|
|
+ 1/2 [TestOutputPlainText]
|
|
+ TestOutputPlainText container
|
|
+
|
|
+ sage: test = TestObject()
|
|
+ sage: test
|
|
+ called the _repr_ method
|
|
+ sage: dm.displayhook(test)
|
|
+ called the _rich_repr_ method [TestOutputPlainText]
|
|
+ TestOutputPlainText container
|
|
+"""
|
|
+
|
|
+#*****************************************************************************
|
|
+# Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com>
|
|
+#
|
|
+# Distributed under the terms of the GNU General Public License (GPL)
|
|
+# as published by the Free Software Foundation; either version 2 of
|
|
+# the License, or (at your option) any later version.
|
|
+# http://www.gnu.org/licenses/
|
|
+#*****************************************************************************
|
|
+
|
|
+
|
|
+from sage.structure.sage_object import SageObject
|
|
+from sage.repl.rich_output.backend_base import BackendBase
|
|
+from sage.repl.rich_output.output_catalog import OutputPlainText, OutputImagePng
|
|
+
|
|
+
|
|
+class TestOutputPlainText(OutputPlainText):
|
|
+
|
|
+ def __init__(self, *args, **kwds):
|
|
+ """
|
|
+ Backend-specific subclass of the plain text output container.
|
|
+
|
|
+ Backends must not influence how the display system constucts
|
|
+ output containers, they can only control how the output
|
|
+ container is displayed. In particular, we cannot override the
|
|
+ constructor (only the
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputPlainText`
|
|
+ constructor is used).
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_test import TestOutputPlainText
|
|
+ sage: TestOutputPlainText()
|
|
+ Traceback (most recent call last):
|
|
+ AssertionError: cannot override constructor
|
|
+ """
|
|
+ raise AssertionError('cannot override constructor')
|
|
+
|
|
+ def print_to_stdout(self):
|
|
+ """
|
|
+ Write the data to stdout.
|
|
+
|
|
+ This is just a convenience method to help with debugging.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: test_output = dm.displayhook(123)
|
|
+ 123 [TestOutputPlainText]
|
|
+ sage: type(test_output)
|
|
+ <class 'sage.repl.rich_output.backend_test.TestOutputPlainText'>
|
|
+ sage: test_output.print_to_stdout()
|
|
+ 123 [TestOutputPlainText]
|
|
+ """
|
|
+ print('{0} [{1}]'.format(self.text.get(), self.__class__.__name__))
|
|
+
|
|
+
|
|
+class TestObject(SageObject):
|
|
+ """
|
|
+ Test object with both :meth:`_repr_` and :meth:`_rich_repr_`
|
|
+ """
|
|
+
|
|
+ def _repr_(self):
|
|
+ """
|
|
+ Return string representation
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_test import TestObject
|
|
+ sage: obj = TestObject()
|
|
+ sage: obj._repr_()
|
|
+ 'called the _repr_ method'
|
|
+ """
|
|
+ return 'called the _repr_ method'
|
|
+
|
|
+ def _rich_repr_(self, display_manager):
|
|
+ """
|
|
+ Rich Output Magic Method
|
|
+
|
|
+ See :mod:`sage.repl.rich_output` for details.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: display_manager = sage.repl.rich_output.get_display_manager()
|
|
+ sage: from sage.repl.rich_output.backend_test import TestObject
|
|
+ sage: obj = TestObject()
|
|
+ sage: rich_output = obj._rich_repr_(display_manager); rich_output
|
|
+ OutputPlainText container
|
|
+ sage: rich_output.text.get()
|
|
+ 'called the _rich_repr_ method'
|
|
+ """
|
|
+ tp = display_manager.types
|
|
+ if tp.OutputPlainText in display_manager.supported_output():
|
|
+ return tp.OutputPlainText('called the _rich_repr_ method')
|
|
+
|
|
+
|
|
+class BackendTest(BackendBase):
|
|
+
|
|
+ def _repr_(self):
|
|
+ """
|
|
+ Return the string representation
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: display_manager = sage.repl.rich_output.get_display_manager()
|
|
+ sage: backend = display_manager._backend
|
|
+ sage: backend._repr_()
|
|
+ 'test'
|
|
+ """
|
|
+ return 'test'
|
|
+
|
|
+ def supported_output(self):
|
|
+ """
|
|
+ Return the outputs that are supported by the backend.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Iterable of output container classes. Only the
|
|
+ :class:`~sage.repl.rich_repr.backend_test.TestOutputPlainText`
|
|
+ output container is supported by the test backend.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: display_manager = sage.repl.rich_output.get_display_manager()
|
|
+ sage: backend = display_manager._backend
|
|
+ sage: backend.supported_output()
|
|
+ set([<class 'sage.repl.rich_output.backend_test.TestOutputPlainText'>])
|
|
+
|
|
+ The output of this method is used by the display manager to
|
|
+ set up the actual supported outputs. Compare::
|
|
+
|
|
+ sage: display_manager.supported_output()
|
|
+ frozenset([<class 'sage.repl.rich_output.output_basic.OutputPlainText'>])
|
|
+ """
|
|
+ return set([TestOutputPlainText])
|
|
+
|
|
+ def display_immediately(self, plain_text, rich_output):
|
|
+ """
|
|
+ Show output without going back to the command line prompt.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ Same as :meth:`displayhook`.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ This method returns the rich output for doctesting
|
|
+ convenience. The actual display framework ignores the return
|
|
+ value.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_basic import OutputPlainText
|
|
+ sage: plain_text = OutputPlainText.example()
|
|
+ sage: from sage.repl.rich_output.backend_test import BackendTest
|
|
+ sage: backend = BackendTest()
|
|
+ sage: backend.display_immediately(plain_text, plain_text)
|
|
+ Example plain text output
|
|
+ OutputPlainText container
|
|
+ """
|
|
+ plain_text.print_to_stdout()
|
|
+ if plain_text is not rich_output:
|
|
+ print('rich output type: [{0}]'.format(rich_output.__class__.__name__))
|
|
+ return rich_output
|
|
diff --git a/src/sage/repl/rich_output/buffer.py b/src/sage/repl/rich_output/buffer.py
|
|
new file mode 100644
|
|
index 0000000..bc82a7e
|
|
--- /dev/null
|
|
+++ b/src/sage/repl/rich_output/buffer.py
|
|
@@ -0,0 +1,254 @@
|
|
+# -*- encoding: utf-8 -*-
|
|
+r"""
|
|
+Output Buffer
|
|
+
|
|
+This is the fundamental unit of rich output, a single immutable buffer
|
|
+(either in-memory or as a file). Rich output always consists of one or
|
|
+more buffers. Ideally, the Sage library always uses the buffer object
|
|
+as an in-memory buffer. But you can also ask it for a filename, and it
|
|
+will save the data to a file if necessary. Either way, the buffer
|
|
+object presents the same interface for getting the content of an
|
|
+in-memory buffer or a temporary file. So any rich output backends do
|
|
+not need to know where the buffer content is actually stored.
|
|
+
|
|
+EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.buffer import OutputBuffer
|
|
+ sage: buf = OutputBuffer('this is the buffer content'); buf
|
|
+ buffer containing 26 bytes
|
|
+ sage: buf.get()
|
|
+ 'this is the buffer content'
|
|
+"""
|
|
+
|
|
+#*****************************************************************************
|
|
+# Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com>
|
|
+#
|
|
+# Distributed under the terms of the GNU General Public License (GPL)
|
|
+# as published by the Free Software Foundation; either version 2 of
|
|
+# the License, or (at your option) any later version.
|
|
+# http://www.gnu.org/licenses/
|
|
+#*****************************************************************************
|
|
+
|
|
+
|
|
+import os
|
|
+from sage.structure.sage_object import SageObject
|
|
+
|
|
+
|
|
+class OutputBuffer(SageObject):
|
|
+
|
|
+ def __init__(self, data):
|
|
+ """
|
|
+ Data stored either in memory or as a file
|
|
+
|
|
+ This class is an abstraction for "files", in that they can
|
|
+ either be defined by a bytes array (Python 3) or string
|
|
+ (Python 2) or by a file (see :meth:`from_file`).
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``data`` -- bytes. The data that is stored in the buffer.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.buffer import OutputBuffer
|
|
+ sage: buf = OutputBuffer('this is the buffer content'); buf
|
|
+ buffer containing 26 bytes
|
|
+
|
|
+ sage: buf2 = OutputBuffer(buf); buf2
|
|
+ buffer containing 26 bytes
|
|
+
|
|
+ sage: buf.get()
|
|
+ 'this is the buffer content'
|
|
+ sage: buf.filename(ext='.txt')
|
|
+ '/....txt'
|
|
+ """
|
|
+ if isinstance(data, OutputBuffer):
|
|
+ self._filename = data._filename
|
|
+ self._data = data._data
|
|
+ else:
|
|
+ self._filename = None
|
|
+ self._data = bytes(data)
|
|
+
|
|
+ @classmethod
|
|
+ def from_file(cls, filename):
|
|
+ """
|
|
+ Construct buffer from data in file.
|
|
+
|
|
+ .. warning::
|
|
+
|
|
+ The buffer assumes that the file content remains the same
|
|
+ during the lifetime of the Sage session. To communicate
|
|
+ this to the user, the file permissions will be changed to
|
|
+ read only.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``filename`` -- string. The filename under which the data is
|
|
+ stored.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String containing the buffer data.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.buffer import OutputBuffer
|
|
+ sage: name = sage.misc.temporary_file.tmp_filename()
|
|
+ sage: with open(name, 'w') as f:
|
|
+ ....: f.write('file content')
|
|
+ sage: buf = OutputBuffer.from_file(name); buf
|
|
+ buffer containing 12 bytes
|
|
+
|
|
+ sage: buf.filename() == name
|
|
+ True
|
|
+ sage: buf.get()
|
|
+ 'file content'
|
|
+ """
|
|
+ buf = cls.__new__(cls)
|
|
+ buf._filename = filename
|
|
+ buf._data = None
|
|
+ buf._chmod_readonly(buf._filename)
|
|
+ return buf
|
|
+
|
|
+ @classmethod
|
|
+ def _chmod_readonly(cls, filename):
|
|
+ """
|
|
+ Make file readonly
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``filename`` -- string. Name of an already-existing file.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.buffer import OutputBuffer
|
|
+ sage: tmp = sage.misc.temporary_file.tmp_filename()
|
|
+ sage: with open(tmp, 'w') as f:
|
|
+ ....: f.write('file content')
|
|
+ sage: OutputBuffer._chmod_readonly(tmp)
|
|
+ sage: import os, stat
|
|
+ sage: stat.S_IMODE(os.stat(tmp).st_mode) & (stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
|
|
+ 0
|
|
+ """
|
|
+ import os
|
|
+ import stat
|
|
+ mode = os.stat(filename).st_mode
|
|
+ mode = stat.S_IMODE(mode) & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
|
|
+ os.chmod(filename, mode)
|
|
+
|
|
+ def _repr_(self):
|
|
+ """
|
|
+ Return a string representation
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.buffer import OutputBuffer
|
|
+ sage: OutputBuffer('test1234')
|
|
+ buffer containing 8 bytes
|
|
+ """
|
|
+ return 'buffer containing {0} bytes'.format(len(self.get()))
|
|
+
|
|
+ def get(self):
|
|
+ """
|
|
+ Return the buffer content
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Bytes. A string in Python 2.x.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.buffer import OutputBuffer
|
|
+ sage: OutputBuffer('test1234').get()
|
|
+ 'test1234'
|
|
+ """
|
|
+ if self._data is None:
|
|
+ with open(self._filename) as f:
|
|
+ self._data = f.read()
|
|
+ return self._data
|
|
+
|
|
+ def filename(self, ext=None):
|
|
+ """
|
|
+ Return the filename.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``ext`` -- string. The file extension.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Name of a file, most likely a temporary file. If ``ext`` is
|
|
+ specified, the filename will have that extension.
|
|
+
|
|
+ You must not modify the returned file. Its permissions are set
|
|
+ to readonly to help with that.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.buffer import OutputBuffer
|
|
+ sage: buf = OutputBuffer('test')
|
|
+ sage: buf.filename() # random output
|
|
+ '/home/user/.sage/temp/hostname/26085/tmp_RNSfAc'
|
|
+
|
|
+ sage: os.path.isfile(buf.filename())
|
|
+ True
|
|
+ sage: buf.filename(ext='txt') # random output
|
|
+ '/home/user/.sage/temp/hostname/26085/tmp_Rjjp4V.txt'
|
|
+ sage: buf.filename(ext='txt').endswith('.txt')
|
|
+ True
|
|
+ """
|
|
+ if ext is None:
|
|
+ ext = ''
|
|
+ elif not ext.startswith('.'):
|
|
+ ext = '.' + ext
|
|
+
|
|
+ if self._filename is None or not self._filename.endswith(ext):
|
|
+ from sage.misc.temporary_file import tmp_filename
|
|
+ output = tmp_filename(ext=ext)
|
|
+ else:
|
|
+ output = self._filename
|
|
+
|
|
+ if self._filename is None:
|
|
+ assert self._data is not None
|
|
+ with open(output, 'wb') as f:
|
|
+ f.write(self._data)
|
|
+ self._filename = output
|
|
+ elif self._filename != output:
|
|
+ try:
|
|
+ os.link(self._filename, output)
|
|
+ except (OSError, AttributeError):
|
|
+ import shutil
|
|
+ shutil.copy2(self._filename, output)
|
|
+
|
|
+ self._chmod_readonly(output)
|
|
+ return output
|
|
+
|
|
+ def save_as(self, filename):
|
|
+ """
|
|
+ Save a copy of the buffer content.
|
|
+
|
|
+ You may edit the returned file, unlike the file returned by
|
|
+ :meth:`filename`.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``filename`` -- string. The file name to save under.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.buffer import OutputBuffer
|
|
+ sage: buf = OutputBuffer('test')
|
|
+ sage: buf.filename(ext='txt') # random output
|
|
+ sage: tmp = tmp_dir()
|
|
+ sage: filename = os.path.join(tmp, 'foo.txt')
|
|
+ sage: buf.save_as(filename)
|
|
+ sage: with open(filename, 'r') as f:
|
|
+ ....: f.read()
|
|
+ 'test'
|
|
+ """
|
|
+ with open(filename, 'w') as f:
|
|
+ f.write(self.get())
|
|
diff --git a/src/sage/repl/rich_output/display_manager.py b/src/sage/repl/rich_output/display_manager.py
|
|
new file mode 100644
|
|
index 0000000..92f6868
|
|
--- /dev/null
|
|
+++ b/src/sage/repl/rich_output/display_manager.py
|
|
@@ -0,0 +1,747 @@
|
|
+# -*- encoding: utf-8 -*-
|
|
+r"""
|
|
+Display Manager
|
|
+
|
|
+This is the heart of the rich output system, the display manager
|
|
+arbitrates between
|
|
+
|
|
+* Backend capabilities: what can be displayed
|
|
+
|
|
+* Backend preferences: what gives good quality on the backend
|
|
+
|
|
+* Sage capabilities: every Sage object can only generate certain
|
|
+ representations, and
|
|
+
|
|
+* User preferences: typeset vs. plain text vs. ascii art, etc.
|
|
+
|
|
+The display manager is a singleton class, Sage always has exactly one
|
|
+instance of it. Use :func:`get_display_manager` to obtain it.
|
|
+
|
|
+EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager(); dm
|
|
+ The Sage display manager using the doctest backend
|
|
+"""
|
|
+
|
|
+#*****************************************************************************
|
|
+# Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com>
|
|
+#
|
|
+# Distributed under the terms of the GNU General Public License (GPL)
|
|
+# as published by the Free Software Foundation; either version 2 of
|
|
+# the License, or (at your option) any later version.
|
|
+# http://www.gnu.org/licenses/
|
|
+#*****************************************************************************
|
|
+
|
|
+
|
|
+import warnings
|
|
+
|
|
+from sage.structure.sage_object import SageObject
|
|
+from sage.repl.rich_output.output_basic import (
|
|
+ OutputPlainText, OutputAsciiArt, OutputLatex,
|
|
+)
|
|
+
|
|
+
|
|
+class DisplayException(Exception):
|
|
+ """
|
|
+ Base exception for all rich output-related exceptions.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.display_manager import DisplayException
|
|
+ sage: raise DisplayException('foo')
|
|
+ Traceback (most recent call last):
|
|
+ ...
|
|
+ DisplayException: foo
|
|
+ """
|
|
+ pass
|
|
+
|
|
+class OutputTypeException(DisplayException):
|
|
+ """
|
|
+ Wrong Output container.
|
|
+
|
|
+ The output containers are the subclasses of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputBase` that
|
|
+ contain the entire output. The display backends must create output
|
|
+ containers of a suitable type depending on the displayed Python
|
|
+ object. This exception indicates that there is a mistake in the
|
|
+ backend and it returned the wrong type of output container.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.display_manager import OutputTypeException
|
|
+ sage: raise OutputTypeException('foo')
|
|
+ Traceback (most recent call last):
|
|
+ ...
|
|
+ OutputTypeException: foo
|
|
+ """
|
|
+ pass
|
|
+
|
|
+class RichReprWarning(UserWarning):
|
|
+ """
|
|
+ Warning that is throws if a call to ``_rich_repr_`` fails.
|
|
+
|
|
+ If an object implements ``_rich_repr_`` then it must return a
|
|
+ value, possibly ``None`` to indicate that no rich output can be
|
|
+ generated. But it may not raise an exception as it is very
|
|
+ confusing for the user if the displayhook fails.
|
|
+
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.display_manager import RichReprWarning
|
|
+ sage: raise RichReprWarning('foo')
|
|
+ Traceback (most recent call last):
|
|
+ ...
|
|
+ RichReprWarning: foo
|
|
+ """
|
|
+ pass
|
|
+
|
|
+
|
|
+class restricted_output(object):
|
|
+
|
|
+ def __init__(self, display_manager, output_classes):
|
|
+ """
|
|
+ Context manager to temporarily restrict the accepted output types
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``display_manager`` -- the display manager.
|
|
+
|
|
+ - ``output_classes`` -- iterable of
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.display_manager import (
|
|
+ ....: get_display_manager, restricted_output)
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: restricted_output(dm, [dm.types.OutputPlainText])
|
|
+ <sage.repl.rich_output.display_manager.restricted_output object at 0x...>
|
|
+ """
|
|
+ self._display_manager = display_manager
|
|
+ self._output_classes = frozenset(output_classes)
|
|
+
|
|
+ def __enter__(self):
|
|
+ """
|
|
+ Enter the restricted output context
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.display_manager import (
|
|
+ ....: get_display_manager, restricted_output)
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: len(dm.supported_output()) > 1
|
|
+ True
|
|
+ sage: with restricted_output(dm, [dm.types.OutputPlainText]):
|
|
+ ....: dm.supported_output()
|
|
+ frozenset({<class 'sage.repl.rich_output.output_basic.OutputPlainText'>})
|
|
+ """
|
|
+ dm = self._display_manager
|
|
+ self._original = dm._supported_output
|
|
+ dm._supported_output = self._output_classes
|
|
+
|
|
+ def __exit__(self, exception_type, value, traceback):
|
|
+ """
|
|
+ Exit the restricted output context
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.display_manager import (
|
|
+ ....: get_display_manager, restricted_output)
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: with restricted_output(dm, [dm.types.OutputPlainText]):
|
|
+ ....: assert len(dm.supported_output()) == 1
|
|
+ sage: assert len(dm.supported_output()) > 1
|
|
+ """
|
|
+ self._display_manager._supported_output = self._original
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+class DisplayManager(SageObject):
|
|
+
|
|
+ _instance = None
|
|
+
|
|
+ def __init__(self):
|
|
+ """
|
|
+ The Display Manager
|
|
+
|
|
+ Used to decide what kind of rich output is best.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: get_display_manager()
|
|
+ The Sage display manager using the doctest backend
|
|
+ """
|
|
+ assert DisplayManager._instance is None
|
|
+ DisplayManager._instance = self
|
|
+ from sage.repl.rich_output.backend_base import BackendSimple
|
|
+ self.switch_backend(BackendSimple())
|
|
+
|
|
+ @classmethod
|
|
+ def get_instance(cls):
|
|
+ """
|
|
+ Get the singleton instance.
|
|
+
|
|
+ This class method is equivalent to
|
|
+ :func:`get_display_manager`.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ The display manager singleton.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.display_manager import DisplayManager
|
|
+ sage: DisplayManager.get_instance()
|
|
+ The Sage display manager using the doctest backend
|
|
+ """
|
|
+ if cls._instance is not None:
|
|
+ return cls._instance
|
|
+ else:
|
|
+ return cls()
|
|
+
|
|
+ def _repr_(self):
|
|
+ """
|
|
+ Return a string representation.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: get_display_manager()
|
|
+ The Sage display manager using the doctest backend
|
|
+ """
|
|
+ s = 'The Sage display manager using the {0} backend'.format(self._backend)
|
|
+ return s
|
|
+
|
|
+ @property
|
|
+ def types(self):
|
|
+ """
|
|
+ Catalog of all output container types.
|
|
+
|
|
+ Note that every output type must be registered in
|
|
+ :mod:`sage.repl.rich_output.output_catalog`.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Returns the :mod:`sage.repl.rich_output.output_catalog`
|
|
+ module.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: dm.types.OutputPlainText
|
|
+ <class 'sage.repl.rich_output.output_basic.OutputPlainText'>
|
|
+ """
|
|
+ import sage.repl.rich_output.output_catalog
|
|
+ return sage.repl.rich_output.output_catalog
|
|
+
|
|
+ def switch_backend(self, backend, **kwds):
|
|
+ """
|
|
+ Switch to a new backend
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``backend`` -- instance of
|
|
+ :class:`~sage.repl.rich_output.backend_base.BackendBase`.
|
|
+
|
|
+ - ``kwds`` -- optional keyword arguments that are passed on to
|
|
+ the
|
|
+ :meth:`~sage.repl.rich_output.backend_base.BackendBase.install`
|
|
+ method.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ The previous backend.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_base import BackendSimple
|
|
+ sage: simple = BackendSimple()
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager(); dm
|
|
+ The Sage display manager using the doctest backend
|
|
+
|
|
+ sage: previous = dm.switch_backend(simple)
|
|
+ sage: dm
|
|
+ The Sage display manager using the simple backend
|
|
+
|
|
+ Restore the doctest backend::
|
|
+
|
|
+ sage: dm.switch_backend(previous) is simple
|
|
+ True
|
|
+ """
|
|
+ from sage.repl.rich_output.backend_base import BackendBase
|
|
+ if not isinstance(backend, BackendBase):
|
|
+ raise ValueError('backend must be instance of BackendBase class')
|
|
+ supported = backend.supported_output()
|
|
+ if not any(issubclass(out, OutputPlainText) for out in supported):
|
|
+ raise ValueError('every backend must support plain text')
|
|
+ try:
|
|
+ self._backend.uninstall()
|
|
+ except AttributeError:
|
|
+ pass # first time we switch
|
|
+ # clear caches
|
|
+ self._output_promotions = dict()
|
|
+ self._supported_output = frozenset(
|
|
+ map(self._demote_output_class, backend.supported_output()))
|
|
+ # install new backend
|
|
+ try:
|
|
+ old_backend = self._backend
|
|
+ except AttributeError:
|
|
+ old_backend = None
|
|
+ self._backend = backend
|
|
+ self._backend.install(**kwds)
|
|
+ from sage.repl.rich_output.preferences import DisplayPreferences
|
|
+ self._preferences = DisplayPreferences(self._backend.default_preferences())
|
|
+ return old_backend
|
|
+
|
|
+ @property
|
|
+ def preferences(self):
|
|
+ """
|
|
+ Return the preferences.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ The display preferences as instance of
|
|
+ :class:`~sage.repl.rich_output.preferences.DisplayPreferences`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: dm.preferences
|
|
+ Display preferences:
|
|
+ * graphics is not specified
|
|
+ * text is not specified
|
|
+ """
|
|
+ return self._preferences
|
|
+
|
|
+ def check_backend_class(self, backend_class):
|
|
+ """
|
|
+ Check that the current backend is an instance of
|
|
+ ``backend_class``.
|
|
+
|
|
+ This is, for example, used by the Sage IPython display
|
|
+ formatter to ensure that the IPython backend is in use.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``backend_class`` -- type of a backend class.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ This method returns nothing. A ``RuntimeError`` is raised if
|
|
+ ``backend_class`` is not the type of the current backend.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.backend_base import BackendSimple
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: dm.check_backend_class(BackendSimple)
|
|
+ Traceback (most recent call last):
|
|
+ ...
|
|
+ RuntimeError: check failed: current backend is invalid
|
|
+ """
|
|
+ if not isinstance(self._backend, backend_class):
|
|
+ raise RuntimeError('check failed: current backend is invalid')
|
|
+
|
|
+ def _demote_output_class(self, output_class):
|
|
+ """
|
|
+ Helper for :meth:`switch_backend`.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``output_class`` -- a possibly derived class from one of the
|
|
+ output container classes in :meth:`types`.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ The underlying container class that it was derived from.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: dm._demote_output_class(dm.types.OutputPlainText)
|
|
+ <class 'sage.repl.rich_output.output_basic.OutputPlainText'>
|
|
+ """
|
|
+ from sage.repl.rich_output.output_basic import OutputBase
|
|
+ if not issubclass(output_class, OutputBase):
|
|
+ raise OutputTypeException(
|
|
+ 'invalid output container type: {0} is not subclass of OutputBase'
|
|
+ .format(output_class))
|
|
+ result = None
|
|
+ for type_name in dir(self.types):
|
|
+ if type_name.startswith('_'):
|
|
+ continue
|
|
+ tp = getattr(self.types, type_name)
|
|
+ if not issubclass(tp, OutputBase):
|
|
+ continue
|
|
+ if issubclass(output_class, tp):
|
|
+ if result is not None:
|
|
+ raise OutputTypeException(
|
|
+ '{0} inherits from multiple output classes'
|
|
+ .format(output_class))
|
|
+ else:
|
|
+ self._output_promotions[tp] = output_class
|
|
+ result = tp
|
|
+ if result is None:
|
|
+ raise OutputTypeException(
|
|
+ '{0} does not inherit from any known output class'
|
|
+ .format(output_class))
|
|
+ return result
|
|
+
|
|
+ def _promote_output(self, output):
|
|
+ """
|
|
+ Promote output container to a backend-specific subclass
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``output`` -- instance of a subclass of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputBase`. Backend-agnostic
|
|
+ output container.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ If the backend returned a subclass of the type of ``output``:
|
|
+ promote the class to the subclass and return it. Otherwise,
|
|
+ the unchanged output is returned.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: out = dm._promote_output(dm.types.OutputPlainText('test'))
|
|
+ sage: type(out)
|
|
+ <class 'sage.repl.rich_output.output_basic.OutputPlainText'>
|
|
+ """
|
|
+ try:
|
|
+ specialized_class = self._output_promotions[type(output)]
|
|
+ except KeyError:
|
|
+ return output
|
|
+ output.__class__ = specialized_class
|
|
+ return output
|
|
+
|
|
+ def _preferred_text_formatter(self, obj, plain_text=None):
|
|
+ """
|
|
+ Return the preferred textual representation
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``obj`` -- anything.
|
|
+
|
|
+ - ``plain_text`` -- ``None`` (default) or string. The plain
|
|
+ text representation. If specified, this will be used for
|
|
+ plain text output.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ One of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputPlainText`,
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputAsciiArt`,
|
|
+ or
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputLatex`
|
|
+ containing the preferred textual representation of ``obj``
|
|
+ supported by the backend.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: dm.preferences.text is None
|
|
+ True
|
|
+ sage: dm._preferred_text_formatter(1/42)
|
|
+ OutputPlainText container
|
|
+
|
|
+ sage: dm.preferences.text = 'plain'
|
|
+ sage: dm._preferred_text_formatter(1/42)
|
|
+ OutputPlainText container
|
|
+
|
|
+ sage: dm.preferences.text = 'ascii_art'
|
|
+ sage: dm._preferred_text_formatter(1/42)
|
|
+ OutputAsciiArt container
|
|
+
|
|
+ sage: dm.preferences.text = 'latex'
|
|
+ sage: dm._preferred_text_formatter(1/42)
|
|
+ \newcommand{\Bold}[1]{\mathbf{#1}}\verb|OutputLatex|\phantom{\verb!x!}\verb|container|
|
|
+
|
|
+ sage: del dm.preferences.text # reset to default
|
|
+ """
|
|
+ want = self.preferences.text
|
|
+ supported = self._backend.supported_output()
|
|
+ if want == 'ascii_art' and OutputAsciiArt in supported:
|
|
+ out = self._backend.ascii_art_formatter(obj)
|
|
+ if type(out) != OutputAsciiArt:
|
|
+ raise OutputTypeException('backend returned wrong output type, require AsciiArt')
|
|
+ return out
|
|
+ if want == 'latex' and OutputLatex in supported:
|
|
+ out = self._backend.latex_formatter(obj)
|
|
+ if type(out) != OutputLatex:
|
|
+ raise OutputTypeException('backend returned wrong output type, require Latex')
|
|
+ return out
|
|
+ if plain_text is not None:
|
|
+ if type(plain_text) != OutputPlainText:
|
|
+ raise OutputTypeException('backend returned wrong output type, require PlainText')
|
|
+ return plain_text
|
|
+ out = self._backend.plain_text_formatter(obj)
|
|
+ if type(out) != OutputPlainText:
|
|
+ raise OutputTypeException('backend returned wrong output type, require PlainText')
|
|
+ return out
|
|
+
|
|
+ def _call_rich_repr(self, obj, rich_repr_kwds):
|
|
+ """
|
|
+ Call the ``_rich_repr_`` method
|
|
+
|
|
+ This method calls ``obj._rich_repr_``. If this raises an
|
|
+ exception, it is caught and a suitable warning is displayed.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``obj`` -- anything.
|
|
+
|
|
+ - ``rich_repr_kwds`` -- dictionary. Optional keyword arguments
|
|
+ that are passed through to ``obj._rich_repr_``.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Whatever ``_rich_repr_`` returned. If it raises an exception,
|
|
+ then a :class:`DisplayFormatterWarning`` is displayed and
|
|
+ ``None`` is returned.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: class Foo(SageObject):
|
|
+ ....: def _rich_repr_(self, display_manager, **kwds):
|
|
+ ....: raise ValueError('reason')
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: dm._call_rich_repr(Foo(), {})
|
|
+ doctest:...: RichReprWarning: Exception in _rich_repr_ while displaying object: reason
|
|
+ """
|
|
+ if rich_repr_kwds:
|
|
+ # do not ignore errors from invalid options
|
|
+ return obj._rich_repr_(self, **rich_repr_kwds)
|
|
+ try:
|
|
+ return obj._rich_repr_(self)
|
|
+ except NotImplementedError as e:
|
|
+ # don't warn on NotImplementedErrors
|
|
+ return None
|
|
+ except Exception as e:
|
|
+ warnings.warn(
|
|
+ 'Exception in _rich_repr_ while displaying object: {0}'.format(e),
|
|
+ RichReprWarning,
|
|
+ )
|
|
+
|
|
+ def _rich_output_formatter(self, obj, rich_repr_kwds):
|
|
+ """
|
|
+ Generate appropriate rich output containers.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``obj`` -- anything.
|
|
+
|
|
+ - ``rich_repr_kwds`` -- dictionary. Optional keyword arguments
|
|
+ that are passed through to ``obj._rich_repr_``.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ A pair of rich output containers. The first is plain text,
|
|
+ that is, an instance of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputPlainText`. The
|
|
+ second is the best rich output available, subject to the
|
|
+ constraints of the backend.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: dm._rich_output_formatter(1/2, dict())
|
|
+ (OutputPlainText container, OutputPlainText container)
|
|
+ """
|
|
+ rich_output = None
|
|
+ plain_text = None
|
|
+ has_rich_repr = isinstance(obj, SageObject) and hasattr(obj, '_rich_repr_')
|
|
+ if has_rich_repr:
|
|
+ rich_output = self._call_rich_repr(obj, rich_repr_kwds)
|
|
+ if isinstance(rich_output, OutputPlainText):
|
|
+ plain_text = rich_output
|
|
+ elif has_rich_repr:
|
|
+ with restricted_output(self, [OutputPlainText]):
|
|
+ plain_text = self._call_rich_repr(obj, rich_repr_kwds)
|
|
+ if plain_text is None:
|
|
+ plain_text = self._backend.plain_text_formatter(obj)
|
|
+ if rich_output is None:
|
|
+ rich_output = self._preferred_text_formatter(obj, plain_text=plain_text)
|
|
+ # promote output container types to backend-specific containers
|
|
+ plain_text = self._promote_output(plain_text)
|
|
+ rich_output = self._promote_output(rich_output)
|
|
+ # check that the output container types are valid for the backend
|
|
+ supported = self._backend.supported_output()
|
|
+ if not (type(plain_text) in supported):
|
|
+ raise OutputTypeException(
|
|
+ 'text output container not supported: {0}'.format(type(plain_text)))
|
|
+ if not (type(rich_output) in supported):
|
|
+ raise OutputTypeException(
|
|
+ 'output container not supported: {0}'.format(type(rich_output)))
|
|
+ return plain_text, rich_output
|
|
+
|
|
+ def graphics_from_save(self, save_function, save_kwds,
|
|
+ file_extension, output_container,
|
|
+ figsize=None, dpi=None):
|
|
+ r"""
|
|
+ Helper to construct graphics.
|
|
+
|
|
+ This method can be used to simplify the implementation of a
|
|
+ ``_rich_repr_`` method of a graphics object if there is
|
|
+ already a function to save graphics to a file.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``save_function`` -- callable that can save graphics to a file
|
|
+ and accepts options like
|
|
+ :meth:`sage.plot.graphics.Graphics.save`.
|
|
+
|
|
+ - ``save_kwds`` -- dictionary. Keyword arguments that are
|
|
+ passed to the save function.
|
|
+
|
|
+ - ``file_extension`` -- string starting with ``'.'``. The file
|
|
+ extension of the graphics file.
|
|
+
|
|
+ - ``output_container`` -- subclass of
|
|
+ :class:`sage.repl.rich_output.output_basic.OutputBase`. The
|
|
+ output container to use. Must be one of the types in
|
|
+ :meth:`supported_output`.
|
|
+
|
|
+ - ``figsize`` -- pair of integers (optional). The desired graphics
|
|
+ size in pixels. Suggested, but need not be respected by the
|
|
+ output.
|
|
+
|
|
+ - ``dpi`` -- integer (optional). The desired resolution in dots
|
|
+ per inch. Suggested, but need not be respected by the output.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Return an instance of ``output_container``.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: plt = plot(sin)
|
|
+ sage: out = dm.graphics_from_save(plt.save, dict(), '.png', dm.types.OutputImagePng)
|
|
+ sage: out
|
|
+ OutputImagePng container
|
|
+ sage: out.png.get().startswith('\x89PNG')
|
|
+ True
|
|
+ sage: out.png.filename() # random
|
|
+ '/home/user/.sage/temp/localhost.localdomain/23903/tmp_pu5woK.png'
|
|
+ """
|
|
+ import os
|
|
+ if not file_extension.startswith(os.path.extsep):
|
|
+ raise ValueError('file_extension must start with a period')
|
|
+ if output_container not in self.supported_output():
|
|
+ raise OutputTypeException('output_container is not supported by backend')
|
|
+ from sage.misc.temporary_file import tmp_filename
|
|
+ filename = tmp_filename(ext=file_extension)
|
|
+ # Call the save_function with the right arguments
|
|
+ kwds = dict(save_kwds)
|
|
+ if figsize is not None:
|
|
+ kwds['figsize'] = figsize
|
|
+ if dpi is not None:
|
|
+ kwds['dpi'] = dpi
|
|
+ save_function(filename, **kwds)
|
|
+ from sage.repl.rich_output.buffer import OutputBuffer
|
|
+ buf = OutputBuffer.from_file(filename)
|
|
+ return output_container(buf)
|
|
+
|
|
+ def supported_output(self):
|
|
+ """
|
|
+ Return the output container classes that can be used.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Frozen set of subclasses of
|
|
+ :class:`~sage.repl.rich_output.output_basic.OutputBase`. If
|
|
+ the backend defines derived container classes, this method
|
|
+ will always return their base classes.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: dm.types.OutputPlainText in dm.supported_output()
|
|
+ True
|
|
+ sage: type(dm.supported_output())
|
|
+ <type 'frozenset'>
|
|
+ """
|
|
+ return self._supported_output
|
|
+
|
|
+ def displayhook(self, obj):
|
|
+ """
|
|
+ Implementation of the displayhook
|
|
+
|
|
+ Every backend must pass the value of the last statement of a
|
|
+ line / cell to this method. See also
|
|
+ :meth:`display_immediately` if you want do display rich output
|
|
+ while a program is running.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``obj`` -- anything. The object to be shown.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Returns whatever the backend's displayhook method returned.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: dm.displayhook(1/2)
|
|
+ 1/2
|
|
+ """
|
|
+ if obj is None:
|
|
+ return
|
|
+ self._backend.set_underscore_variable(obj)
|
|
+ plain_text, rich_output = self._rich_output_formatter(obj, dict())
|
|
+ return self._backend.displayhook(plain_text, rich_output)
|
|
+
|
|
+ def display_immediately(self, obj, **rich_repr_kwds):
|
|
+ """
|
|
+ Show output without going back to the command line prompt.
|
|
+
|
|
+ This method must be called to create rich output from an
|
|
+ object when we are not returning to the command line prompt,
|
|
+ for example during program execution. Typically, it is being
|
|
+ called by :meth:`sage.plot.graphics.Graphics.show`.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``obj`` -- anything. The object to be shown.
|
|
+
|
|
+ - ``rich_repr_kwds`` -- optional keyword arguments that are
|
|
+ passed through to ``obj._rich_repr_``.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output import get_display_manager
|
|
+ sage: dm = get_display_manager()
|
|
+ sage: dm.display_immediately(1/2)
|
|
+ 1/2
|
|
+ """
|
|
+ plain_text, rich_output = self._rich_output_formatter(obj, rich_repr_kwds)
|
|
+ self._backend.display_immediately(plain_text, rich_output)
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+get_display_manager = DisplayManager.get_instance
|
|
diff --git a/src/sage/repl/rich_output/output_basic.py b/src/sage/repl/rich_output/output_basic.py
|
|
new file mode 100644
|
|
index 0000000..f445071
|
|
--- /dev/null
|
|
+++ b/src/sage/repl/rich_output/output_basic.py
|
|
@@ -0,0 +1,350 @@
|
|
+# -*- encoding: utf-8 -*-
|
|
+r"""
|
|
+Basic Output Types
|
|
+
|
|
+The Sage rich representation system requires a special container class
|
|
+to hold the data for each type of rich output. They all inherit from
|
|
+:class:`OutputBase`, though a more typical example is
|
|
+:class:`OutputPlainText`. Some output classes consist of more than one
|
|
+data buffer, for example jmol or certain animation formats. The output
|
|
+class is independent of user preferences and of the display
|
|
+backend.
|
|
+
|
|
+The display backends can define derived classes to attach
|
|
+backend-specific display functionality to, for example how to launch a
|
|
+viewer. But they must not change how the output container is
|
|
+created. To enforce this, the Sage ``_rich_repr_`` magic method will
|
|
+only ever see the output class defined here. The display manager will
|
|
+promote it to a backend-specific subclass if necessary prior to
|
|
+displaying it.
|
|
+
|
|
+To create new types of output, you must create your own subclass of
|
|
+:class:`OutputBase` and register it in
|
|
+:mod:`sage.repl.rich_output.output_catalog`.
|
|
+
|
|
+.. warning::
|
|
+
|
|
+ All rich output data in sublasses of :class:`OutputBase` must be
|
|
+ contained in :class:`~sage.repl.rich_output.buffer.OutputBuffer`
|
|
+ instances. You must never reference any files on the local file
|
|
+ system, as there is no guarantee that the notebook server and the
|
|
+ worker process are on the same computer. Or even share a common
|
|
+ file system.
|
|
+"""
|
|
+
|
|
+#*****************************************************************************
|
|
+# Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com>
|
|
+#
|
|
+# Distributed under the terms of the GNU General Public License (GPL)
|
|
+# as published by the Free Software Foundation; either version 2 of
|
|
+# the License, or (at your option) any later version.
|
|
+# http://www.gnu.org/licenses/
|
|
+#*****************************************************************************
|
|
+
|
|
+
|
|
+from sage.structure.sage_object import SageObject
|
|
+from sage.repl.rich_output.buffer import OutputBuffer
|
|
+
|
|
+
|
|
+class OutputBase(SageObject):
|
|
+ """
|
|
+ Base class for all rich output containers.
|
|
+ """
|
|
+
|
|
+ def _repr_(self):
|
|
+ """
|
|
+ Return a string representation.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_basic import OutputBase
|
|
+ sage: output_base = OutputBase()
|
|
+ sage: output_base._repr_()
|
|
+ 'OutputBase container'
|
|
+ """
|
|
+ return '{0} container'.format(self.__class__.__name__)
|
|
+
|
|
+ @classmethod
|
|
+ def example(cls):
|
|
+ """
|
|
+ Construct a sample instance
|
|
+
|
|
+ This static method is meant for doctests, so they can easily
|
|
+ construt an example.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ An instance of the :class:`OutputBase` subclass.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_basic import OutputBase
|
|
+ sage: OutputBase.example()
|
|
+ Traceback (most recent call last):
|
|
+ ...
|
|
+ NotImplementedError: derived classes must implement this class method
|
|
+ """
|
|
+ raise NotImplementedError('derived classes must implement this class method')
|
|
+
|
|
+
|
|
+class OutputPlainText(OutputBase):
|
|
+
|
|
+ def __init__(self, plain_text):
|
|
+ """
|
|
+ Plain Text Output
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``plain_text`` --
|
|
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Alternatively,
|
|
+ a string (bytes) can be passed directly which will then be
|
|
+ converted into an
|
|
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. The
|
|
+ plain text output.
|
|
+
|
|
+ This should always be exactly the same as the (non-rich)
|
|
+ output from the ``_repr_`` method. Every backend object must
|
|
+ support plain text output as fallback.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputPlainText
|
|
+ sage: OutputPlainText('foo')
|
|
+ OutputPlainText container
|
|
+ """
|
|
+ self.text = OutputBuffer(plain_text)
|
|
+
|
|
+ @classmethod
|
|
+ def example(cls):
|
|
+ """
|
|
+ Construct a sample plain text output container
|
|
+
|
|
+ This static method is meant for doctests, so they can easily
|
|
+ construt an example.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ An instance of :class:`OutputPlainText`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputPlainText
|
|
+ sage: OutputPlainText.example()
|
|
+ OutputPlainText container
|
|
+ sage: OutputPlainText.example().text.get()
|
|
+ 'Example plain text output'
|
|
+ """
|
|
+ return cls('Example plain text output')
|
|
+
|
|
+ def print_to_stdout(self):
|
|
+ """
|
|
+ Write the data to stdout.
|
|
+
|
|
+ This is just a convenience method to help with debugging.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputPlainText
|
|
+ sage: plain_text = OutputPlainText.example()
|
|
+ sage: plain_text.print_to_stdout()
|
|
+ Example plain text output
|
|
+ """
|
|
+ print(self.text.get())
|
|
+
|
|
+
|
|
+class OutputAsciiArt(OutputBase):
|
|
+
|
|
+ def __init__(self, ascii_art):
|
|
+ """
|
|
+ ASCII Art Output
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``ascii_art`` --
|
|
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Alternatively,
|
|
+ a string (bytes) can be passed directly which will then be
|
|
+ converted into an
|
|
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Ascii
|
|
+ art rendered into a string.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputAsciiArt
|
|
+ sage: OutputAsciiArt(':-}')
|
|
+ OutputAsciiArt container
|
|
+ """
|
|
+ self.ascii_art = OutputBuffer(ascii_art)
|
|
+
|
|
+ @classmethod
|
|
+ def example(cls):
|
|
+ r"""
|
|
+ Construct a sample ascii art output container
|
|
+
|
|
+ This static method is meant for doctests, so they can easily
|
|
+ construt an example.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ An instance of :class:`OutputAsciiArt`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputAsciiArt
|
|
+ sage: OutputAsciiArt.example()
|
|
+ OutputAsciiArt container
|
|
+ sage: OutputAsciiArt.example().ascii_art.get()
|
|
+ '[ * * * * ]\n[ ** ** * * * * * * ]\n[ ***, * , * , **, ** , *, * , * , * ]'
|
|
+ """
|
|
+ return cls('[ * * * * ]\n'
|
|
+ '[ ** ** * * * * * * ]\n'
|
|
+ '[ ***, * , * , **, ** , *, * , * , * ]')
|
|
+
|
|
+ def print_to_stdout(self):
|
|
+ """
|
|
+ Write the data to stdout.
|
|
+
|
|
+ This is just a convenience method to help with debugging.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputAsciiArt
|
|
+ sage: ascii_art = OutputAsciiArt.example()
|
|
+ sage: ascii_art.print_to_stdout()
|
|
+ [ * * * * ]
|
|
+ [ ** ** * * * * * * ]
|
|
+ [ ***, * , * , **, ** , *, * , * , * ]
|
|
+ """
|
|
+ print(self.ascii_art.get())
|
|
+
|
|
+
|
|
+class OutputLatex(OutputBase):
|
|
+
|
|
+ def __init__(self, latex):
|
|
+ """
|
|
+ LaTeX Output
|
|
+
|
|
+ .. note::
|
|
+
|
|
+ The LaTeX commands will only use a subset of LaTeX that
|
|
+ can be displayed by MathJax.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``latex`` --
|
|
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Alternatively,
|
|
+ a string (bytes) can be passed directly which will then be
|
|
+ converted into an
|
|
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. String
|
|
+ containing the latex equation code. Excludes the surrounding
|
|
+ dollar signs / LaTeX equation environment. Also excludes the
|
|
+ surrounding MathJax ``<html>`` tag.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputLatex
|
|
+ sage: OutputLatex('<html><script type="math/tex; mode=display">1</script></html>')
|
|
+ OutputLatex container
|
|
+ """
|
|
+ self.latex = OutputBuffer(latex)
|
|
+
|
|
+ def mathjax(self):
|
|
+ r"""
|
|
+ Return the LaTeX with a surrounding MathJax HTML code.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputLatex
|
|
+ sage: rich_output = OutputLatex('1')
|
|
+ sage: rich_output.latex
|
|
+ buffer containing 1 bytes
|
|
+ sage: rich_output.latex.get()
|
|
+ '1'
|
|
+ sage: rich_output.mathjax()
|
|
+ '<html><script type="math/tex; mode=display">1</script></html>'
|
|
+ """
|
|
+ return r'<html><script type="math/tex; mode=display">{0}</script></html>'.format(
|
|
+ self.latex.get())
|
|
+
|
|
+ def display_equation(self):
|
|
+ r"""
|
|
+ Return the LaTeX code for a display equation
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputLatex
|
|
+ sage: rich_output = OutputLatex('1')
|
|
+ sage: rich_output.latex
|
|
+ buffer containing 1 bytes
|
|
+ sage: rich_output.latex.get()
|
|
+ '1'
|
|
+ sage: rich_output.display_equation()
|
|
+ '\\begin{equation}\n1\n\\end{equation}'
|
|
+ """
|
|
+ return '\n'.join([r'\begin{equation}', self.latex.get(), r'\end{equation}'])
|
|
+
|
|
+ def inline_equation(self):
|
|
+ r"""
|
|
+ Return the LaTeX code for an inline equation
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputLatex
|
|
+ sage: rich_output = OutputLatex('1')
|
|
+ sage: rich_output.latex
|
|
+ buffer containing 1 bytes
|
|
+ sage: rich_output.latex.get()
|
|
+ '1'
|
|
+ sage: rich_output.inline_equation()
|
|
+ '\\begin{math}\n1\n\\end{math}'
|
|
+ """
|
|
+ return '\n'.join([r'\begin{math}', self.latex.get(), r'\end{math}'])
|
|
+
|
|
+ @classmethod
|
|
+ def example(cls):
|
|
+ r"""
|
|
+ Construct a sample LaTeX output container
|
|
+
|
|
+ This static method is meant for doctests, so they can easily
|
|
+ construt an example.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ An instance of :class:`OutputLatex`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputLatex
|
|
+ sage: OutputLatex.example()
|
|
+ OutputLatex container
|
|
+ sage: OutputLatex.example().latex.get()
|
|
+ '\\newcommand{\\Bold}[1]{\\mathbf{#1}}\\int \\sin\\left(x\\right)\\,{d x}'
|
|
+ """
|
|
+ return cls(r'\newcommand{\Bold}[1]{\mathbf{#1}}'
|
|
+ r'\int \sin\left(x\right)\,{d x}')
|
|
+
|
|
+ def print_to_stdout(self):
|
|
+ r"""
|
|
+ Write the data to stdout.
|
|
+
|
|
+ This is just a convenience method to help with debugging.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputLatex
|
|
+ sage: rich_output = OutputLatex.example()
|
|
+ sage: rich_output.print_to_stdout()
|
|
+ \newcommand{\Bold}[1]{\mathbf{#1}}\int \sin\left(x\right)\,{d x}
|
|
+ """
|
|
+ print(self.latex.get())
|
|
diff --git a/src/sage/repl/rich_output/output_catalog.py b/src/sage/repl/rich_output/output_catalog.py
|
|
new file mode 100644
|
|
index 0000000..533f4d1
|
|
--- /dev/null
|
|
+++ b/src/sage/repl/rich_output/output_catalog.py
|
|
@@ -0,0 +1,37 @@
|
|
+# -*- encoding: utf-8 -*-
|
|
+r"""
|
|
+Catalog of all available output container types.
|
|
+
|
|
+If you define another output type then you must add it to the imports here.
|
|
+"""
|
|
+
|
|
+#*****************************************************************************
|
|
+# Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com>
|
|
+#
|
|
+# Distributed under the terms of the GNU General Public License (GPL)
|
|
+# as published by the Free Software Foundation; either version 2 of
|
|
+# the License, or (at your option) any later version.
|
|
+# http://www.gnu.org/licenses/
|
|
+#*****************************************************************************
|
|
+
|
|
+
|
|
+from .output_basic import (
|
|
+ OutputPlainText,
|
|
+ OutputAsciiArt,
|
|
+ OutputLatex,
|
|
+)
|
|
+
|
|
+from .output_graphics import (
|
|
+ OutputImagePng,
|
|
+ OutputImageGif,
|
|
+ OutputImageJpg,
|
|
+ OutputImageSvg,
|
|
+ OutputImagePdf,
|
|
+ OutputImageDvi,
|
|
+)
|
|
+
|
|
+from .output_graphics3d import (
|
|
+ OutputSceneJmol,
|
|
+ OutputSceneWavefront,
|
|
+ OutputSceneCanvas3d,
|
|
+)
|
|
diff --git a/src/sage/repl/rich_output/output_graphics.py b/src/sage/repl/rich_output/output_graphics.py
|
|
new file mode 100644
|
|
index 0000000..c6cc3f4
|
|
--- /dev/null
|
|
+++ b/src/sage/repl/rich_output/output_graphics.py
|
|
@@ -0,0 +1,334 @@
|
|
+# -*- encoding: utf-8 -*-
|
|
+r"""
|
|
+Graphics Output Types
|
|
+
|
|
+This module defines the rich output types for 2-d images, both vector
|
|
+and raster graphics.
|
|
+"""
|
|
+
|
|
+#*****************************************************************************
|
|
+# Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com>
|
|
+#
|
|
+# Distributed under the terms of the GNU General Public License (GPL)
|
|
+# as published by the Free Software Foundation; either version 2 of
|
|
+# the License, or (at your option) any later version.
|
|
+# http://www.gnu.org/licenses/
|
|
+#*****************************************************************************
|
|
+
|
|
+
|
|
+import os
|
|
+
|
|
+from sage.repl.rich_output.output_basic import OutputBase
|
|
+from sage.repl.rich_output.buffer import OutputBuffer
|
|
+
|
|
+
|
|
+class OutputImagePng(OutputBase):
|
|
+
|
|
+ def __init__(self, png):
|
|
+ """
|
|
+ PNG Image
|
|
+
|
|
+ .. NOTE::
|
|
+
|
|
+ Every backend that is capable of displaying any kind of
|
|
+ graphics is supposed to support the PNG format at least.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``png`` --
|
|
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Alternatively,
|
|
+ a string (bytes) can be passed directly which will then be
|
|
+ converted into an
|
|
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. The
|
|
+ PNG image data.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputImagePng
|
|
+ sage: OutputImagePng.example() # indirect doctest
|
|
+ OutputImagePng container
|
|
+ """
|
|
+ self.png = OutputBuffer(png)
|
|
+
|
|
+ @classmethod
|
|
+ def example(cls):
|
|
+ r"""
|
|
+ Construct a sample PNG output container
|
|
+
|
|
+ This static method is meant for doctests, so they can easily
|
|
+ construct an example.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ An instance of :class:`OutputImagePng`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputImagePng
|
|
+ sage: OutputImagePng.example()
|
|
+ OutputImagePng container
|
|
+ sage: OutputImagePng.example().png
|
|
+ buffer containing 608 bytes
|
|
+ sage: OutputImagePng.example().png.get().startswith('\x89PNG')
|
|
+ True
|
|
+ """
|
|
+ from sage.env import SAGE_EXTCODE
|
|
+ filename = os.path.join(SAGE_EXTCODE, 'doctest', 'rich_output', 'example.png')
|
|
+ with open(filename) as f:
|
|
+ return cls(f.read())
|
|
+
|
|
+
|
|
+class OutputImageGif(OutputBase):
|
|
+
|
|
+ def __init__(self, gif):
|
|
+ """
|
|
+ GIF Image (possibly animated)
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``gif`` --
|
|
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Alternatively,
|
|
+ a string (bytes) can be passed directly which will then be
|
|
+ converted into an
|
|
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. The
|
|
+ GIF image data.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputImageGif
|
|
+ sage: OutputImageGif.example() # indirect doctest
|
|
+ OutputImageGif container
|
|
+ """
|
|
+ self.gif = OutputBuffer(gif)
|
|
+
|
|
+ @classmethod
|
|
+ def example(cls):
|
|
+ r"""
|
|
+ Construct a sample GIF output container
|
|
+
|
|
+ This static method is meant for doctests, so they can easily
|
|
+ construct an example.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ An instance of :class:`OutputImageGif`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputImageGif
|
|
+ sage: OutputImageGif.example()
|
|
+ OutputImageGif container
|
|
+ sage: OutputImageGif.example().gif
|
|
+ buffer containing 408 bytes
|
|
+ sage: OutputImageGif.example().gif.get().startswith('GIF89a')
|
|
+ True
|
|
+ """
|
|
+ from sage.env import SAGE_EXTCODE
|
|
+ filename = os.path.join(SAGE_EXTCODE, 'doctest', 'rich_output', 'example.gif')
|
|
+ with open(filename) as f:
|
|
+ return cls(f.read())
|
|
+
|
|
+
|
|
+class OutputImageJpg(OutputBase):
|
|
+
|
|
+ def __init__(self, jpg):
|
|
+ """
|
|
+ JPEG Image
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``jpeg`` --
|
|
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Alternatively,
|
|
+ a string (bytes) can be passed directly which will then be
|
|
+ converted into an
|
|
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. The
|
|
+ JPEG image data.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputImageJpg
|
|
+ sage: OutputImageJpg.example() # indirect doctest
|
|
+ OutputImageJpg container
|
|
+ """
|
|
+ self.jpg = OutputBuffer(jpg)
|
|
+
|
|
+ @classmethod
|
|
+ def example(cls):
|
|
+ r"""
|
|
+ Construct a sample JPEG output container
|
|
+
|
|
+ This static method is meant for doctests, so they can easily
|
|
+ construct an example.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ An instance of :class:`OutputImageJpg`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputImageJpg
|
|
+ sage: OutputImageJpg.example()
|
|
+ OutputImageJpg container
|
|
+ sage: OutputImageJpg.example().jpg
|
|
+ buffer containing 978 bytes
|
|
+ sage: OutputImageJpg.example().jpg.get().startswith('\xff\xd8\xff\xe0\x00\x10JFIF')
|
|
+ True
|
|
+ """
|
|
+ from sage.env import SAGE_EXTCODE
|
|
+ filename = os.path.join(SAGE_EXTCODE, 'doctest', 'rich_output', 'example.jpg')
|
|
+ with open(filename) as f:
|
|
+ return cls(f.read())
|
|
+
|
|
+
|
|
+class OutputImageSvg(OutputBase):
|
|
+
|
|
+ def __init__(self, svg):
|
|
+ """
|
|
+ SVG Image
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``SVG`` --
|
|
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Alternatively,
|
|
+ a string (bytes) can be passed directly which will then be
|
|
+ converted into an
|
|
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. The
|
|
+ SVG image data.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputImageSvg
|
|
+ sage: OutputImageSvg.example() # indirect doctest
|
|
+ OutputImageSvg container
|
|
+ """
|
|
+ self.svg = OutputBuffer(svg)
|
|
+
|
|
+ @classmethod
|
|
+ def example(cls):
|
|
+ r"""
|
|
+ Construct a sample SVG output container
|
|
+
|
|
+ This static method is meant for doctests, so they can easily
|
|
+ construct an example.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ An instance of :class:`OutputImageSvg`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputImageSvg
|
|
+ sage: OutputImageSvg.example()
|
|
+ OutputImageSvg container
|
|
+ sage: OutputImageSvg.example().svg
|
|
+ buffer containing 1422 bytes
|
|
+ sage: '</svg>' in OutputImageSvg.example().svg.get()
|
|
+ True
|
|
+ """
|
|
+ from sage.env import SAGE_EXTCODE
|
|
+ filename = os.path.join(SAGE_EXTCODE, 'doctest', 'rich_output', 'example.svg')
|
|
+ with open(filename) as f:
|
|
+ return cls(f.read())
|
|
+
|
|
+
|
|
+class OutputImagePdf(OutputBase):
|
|
+
|
|
+ def __init__(self, pdf):
|
|
+ """
|
|
+ PDF Image
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``pdf`` --
|
|
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Alternatively,
|
|
+ a string (bytes) can be passed directly which will then be
|
|
+ converted into an
|
|
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. The
|
|
+ PDF data.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputImagePdf
|
|
+ sage: OutputImagePdf.example() # indirect doctest
|
|
+ OutputImagePdf container
|
|
+ """
|
|
+ self.pdf = OutputBuffer(pdf)
|
|
+
|
|
+ @classmethod
|
|
+ def example(cls):
|
|
+ r"""
|
|
+ Construct a sample PDF output container
|
|
+
|
|
+ This static method is meant for doctests, so they can easily
|
|
+ construct an example.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ An instance of :class:`OutputImagePdf`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputImagePdf
|
|
+ sage: OutputImagePdf.example()
|
|
+ OutputImagePdf container
|
|
+ sage: OutputImagePdf.example().pdf
|
|
+ buffer containing 4285 bytes
|
|
+ sage: OutputImagePdf.example().pdf.get().startswith('%PDF-1.4')
|
|
+ True
|
|
+ """
|
|
+ from sage.env import SAGE_EXTCODE
|
|
+ filename = os.path.join(SAGE_EXTCODE, 'doctest', 'rich_output', 'example.pdf')
|
|
+ with open(filename) as f:
|
|
+ return cls(f.read())
|
|
+
|
|
+
|
|
+class OutputImageDvi(OutputBase):
|
|
+
|
|
+ def __init__(self, dvi):
|
|
+ """
|
|
+ DVI Image
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``dvi`` --
|
|
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Alternatively,
|
|
+ a string (bytes) can be passed directly which will then be
|
|
+ converted into an
|
|
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. The
|
|
+ DVI data.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputImageDvi
|
|
+ sage: OutputImageDvi.example() # indirect doctest
|
|
+ OutputImageDvi container
|
|
+ """
|
|
+ self.dvi = OutputBuffer(dvi)
|
|
+
|
|
+ @classmethod
|
|
+ def example(cls):
|
|
+ r"""
|
|
+ Construct a sample DVI output container
|
|
+
|
|
+ This static method is meant for doctests, so they can easily
|
|
+ construct an example.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ An instance of :class:`OutputImageDvi`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputImageDvi
|
|
+ sage: OutputImageDvi.example()
|
|
+ OutputImageDvi container
|
|
+ sage: OutputImageDvi.example().dvi
|
|
+ buffer containing 212 bytes
|
|
+ sage: 'TeX output' in OutputImageDvi.example().dvi.get()
|
|
+ True
|
|
+ """
|
|
+ from sage.env import SAGE_EXTCODE
|
|
+ filename = os.path.join(SAGE_EXTCODE, 'doctest', 'rich_output', 'example.dvi')
|
|
+ with open(filename) as f:
|
|
+ return cls(f.read())
|
|
+
|
|
diff --git a/src/sage/repl/rich_output/output_graphics3d.py b/src/sage/repl/rich_output/output_graphics3d.py
|
|
new file mode 100644
|
|
index 0000000..e98f90e
|
|
--- /dev/null
|
|
+++ b/src/sage/repl/rich_output/output_graphics3d.py
|
|
@@ -0,0 +1,343 @@
|
|
+# -*- encoding: utf-8 -*-
|
|
+r"""
|
|
+Three-Dimensional Graphics Output Types
|
|
+
|
|
+This module defines the rich output types for 3-d scenes.
|
|
+"""
|
|
+
|
|
+#*****************************************************************************
|
|
+# Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com>
|
|
+#
|
|
+# Distributed under the terms of the GNU General Public License (GPL)
|
|
+# as published by the Free Software Foundation; either version 2 of
|
|
+# the License, or (at your option) any later version.
|
|
+# http://www.gnu.org/licenses/
|
|
+#*****************************************************************************
|
|
+
|
|
+
|
|
+import os
|
|
+
|
|
+from sage.repl.rich_output.output_basic import OutputBase
|
|
+from sage.repl.rich_output.buffer import OutputBuffer
|
|
+
|
|
+
|
|
+
|
|
+class OutputSceneJmol(OutputBase):
|
|
+
|
|
+ def __init__(self, scene_zip, preview_png):
|
|
+ """
|
|
+ JMol Scene
|
|
+
|
|
+ By our (Sage) convention, the actual scene is called ``SCENE``
|
|
+ inside the zip archive.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``scene_zip`` -- string/bytes. The jmol scene (a zip archive).
|
|
+
|
|
+ - ``preview_png`` -- string/bytes. Preview as png file.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputSceneJmol
|
|
+ sage: OutputSceneJmol.example()
|
|
+ OutputSceneJmol container
|
|
+ """
|
|
+ self.scene_zip = OutputBuffer(scene_zip)
|
|
+ self.preview_png = OutputBuffer(preview_png)
|
|
+
|
|
+ def launch_script_filename(self):
|
|
+ """
|
|
+ Return a launch script suitable to display the scene.
|
|
+
|
|
+ This method saves the scene to disk and creates a launch
|
|
+ script. The latter contains an absolute path to the scene
|
|
+ file. The launch script is often necessary to make jmol
|
|
+ render the 3d scene.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String. The file name of a suitable launch script.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputSceneJmol
|
|
+ sage: rich_output = OutputSceneJmol.example(); rich_output
|
|
+ OutputSceneJmol container
|
|
+ sage: filename = rich_output.launch_script_filename(); filename
|
|
+ '/.../scene.spt'
|
|
+ sage: print(open(filename).read())
|
|
+ set defaultdirectory "/.../scene.spt.zip"
|
|
+ script SCRIPT
|
|
+ """
|
|
+ from sage.misc.temporary_file import tmp_dir
|
|
+ basedir = tmp_dir()
|
|
+ scene_filename = os.path.join(basedir, 'scene.spt.zip')
|
|
+ script_filename = os.path.join(basedir, 'scene.spt')
|
|
+ self.scene_zip.save_as(scene_filename)
|
|
+ with open(script_filename, 'w') as f:
|
|
+ f.write('set defaultdirectory "{0}"\n'.format(scene_filename))
|
|
+ f.write('script SCRIPT\n')
|
|
+ return script_filename
|
|
+
|
|
+ @classmethod
|
|
+ def example(cls):
|
|
+ r"""
|
|
+ Construct a sample Jmol output container
|
|
+
|
|
+ This static method is meant for doctests, so they can easily
|
|
+ construct an example.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ An instance of :class:`OutputSceneJmol`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputSceneJmol
|
|
+ sage: rich_output = OutputSceneJmol.example(); rich_output
|
|
+ OutputSceneJmol container
|
|
+
|
|
+ sage: rich_output.scene_zip
|
|
+ buffer containing 654 bytes
|
|
+ sage: rich_output.scene_zip.get().startswith('PK')
|
|
+ True
|
|
+
|
|
+ sage: rich_output.preview_png
|
|
+ buffer containing 608 bytes
|
|
+ sage: rich_output.preview_png.get().startswith('\x89PNG')
|
|
+ True
|
|
+ """
|
|
+ from sage.env import SAGE_EXTCODE
|
|
+ example_png_filename = os.path.join(
|
|
+ SAGE_EXTCODE, 'doctest', 'rich_output', 'example.png')
|
|
+ with open(example_png_filename) as f:
|
|
+ example_png = f.read()
|
|
+ scene_zip_filename = os.path.join(
|
|
+ SAGE_EXTCODE, 'doctest', 'rich_output', 'example_jmol.spt.zip')
|
|
+ with open(scene_zip_filename) as f:
|
|
+ scene_zip = f.read()
|
|
+ return cls(scene_zip, example_png)
|
|
+
|
|
+
|
|
+class OutputSceneCanvas3d(OutputBase):
|
|
+
|
|
+ def __init__(self, canvas3d):
|
|
+ """
|
|
+ Canvas3d Scene
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``canvas3d`` -- string/bytes. The canvas3d data.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputSceneCanvas3d
|
|
+ sage: OutputSceneCanvas3d.example()
|
|
+ OutputSceneCanvas3d container
|
|
+ """
|
|
+ self.canvas3d = OutputBuffer(canvas3d)
|
|
+
|
|
+ @classmethod
|
|
+ def example(cls):
|
|
+ r"""
|
|
+ Construct a sample Canvas3D output container
|
|
+
|
|
+ This static method is meant for doctests, so they can easily
|
|
+ construct an example.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ An instance of :class:`OutputSceneCanvas3d`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputSceneCanvas3d
|
|
+ sage: rich_output = OutputSceneCanvas3d.example(); rich_output
|
|
+ OutputSceneCanvas3d container
|
|
+
|
|
+ sage: rich_output.canvas3d
|
|
+ buffer containing 649 bytes
|
|
+ sage: rich_output.canvas3d.get()
|
|
+ "[{vertices:[{x:1,y:1,z:1},...{x:1,y:-1,z:-1}],faces:[[0,1,2,3]],color:'008000'}]"
|
|
+ """
|
|
+ from sage.env import SAGE_EXTCODE
|
|
+ filename = os.path.join(
|
|
+ SAGE_EXTCODE, 'doctest', 'rich_output', 'example.canvas3d')
|
|
+ return cls(OutputBuffer.from_file(filename))
|
|
+
|
|
+
|
|
+class OutputSceneWavefront(OutputBase):
|
|
+
|
|
+ def __init__(self, obj, mtl):
|
|
+ """
|
|
+ Wavefront `*.obj` Scene
|
|
+
|
|
+ The Wavefront format consists of two files, an ``.obj`` file
|
|
+ defining the geometry data (mesh points, normal vectors, ...)
|
|
+ together with a ``.mtl`` file defining texture data.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``obj`` -- bytes. The Wavefront obj file format describing
|
|
+ the mesh shape.
|
|
+
|
|
+ - ``mtl`` -- bytes. The Wavefront mtl file format describing
|
|
+ textures.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputSceneWavefront
|
|
+ sage: OutputSceneWavefront.example()
|
|
+ OutputSceneWavefront container
|
|
+ """
|
|
+ self.obj = OutputBuffer(obj)
|
|
+ self.mtl = OutputBuffer(mtl)
|
|
+ self._check_no_directory(self.mtllib())
|
|
+
|
|
+ def _check_no_directory(self, filename):
|
|
+ """
|
|
+ Verify that ``filename`` does not contain a path.
|
|
+
|
|
+ We disallow anything but plain filenames since it is a
|
|
+ potential security issue to point :meth:`mtllib` at random
|
|
+ paths.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``filename`` -- string. A filename.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ This method returns nothing. A ``ValueError`` is raised if
|
|
+ ``filename`` is not just a plain filename but contains a
|
|
+ directory (relative or absolute).
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputSceneWavefront
|
|
+ sage: rich_output = OutputSceneWavefront.example()
|
|
+ sage: rich_output._check_no_directory('scene.mtl')
|
|
+ sage: rich_output._check_no_directory('/scene.mtl')
|
|
+ Traceback (most recent call last):
|
|
+ ...
|
|
+ ValueError: must be pure filename, got directory component: /scene.mtl
|
|
+ sage: rich_output._check_no_directory('relative/scene.mtl')
|
|
+ Traceback (most recent call last):
|
|
+ ...
|
|
+ ValueError: must be pure filename, got directory component: relative/scene.mtl
|
|
+ sage: rich_output._check_no_directory('/absolute/scene.mtl')
|
|
+ Traceback (most recent call last):
|
|
+ ...
|
|
+ ValueError: must be pure filename, got directory component: /absolute/scene.mtl
|
|
+ """
|
|
+ if os.path.split(filename)[0]:
|
|
+ raise ValueError('must be pure filename, got directory component: {0}'
|
|
+ .format(filename))
|
|
+
|
|
+ def mtllib(self):
|
|
+ """
|
|
+ Return the ``mtllib`` filename
|
|
+
|
|
+ The ``mtllib`` line in the Wavefront file format (``*.obj``)
|
|
+ is the name of the separate texture file.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String. The filename under which ``mtl`` is supposed to be
|
|
+ saved.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputSceneWavefront
|
|
+ sage: rich_output = OutputSceneWavefront.example()
|
|
+ sage: rich_output.mtllib()
|
|
+ 'scene.mtl'
|
|
+ """
|
|
+ marker = 'mtllib '
|
|
+ for line in self.obj.get().splitlines():
|
|
+ if line.startswith(marker):
|
|
+ return line[len(marker):]
|
|
+ return 'scene.mtl'
|
|
+
|
|
+ def obj_filename(self):
|
|
+ """
|
|
+ Return the file name of the ``.obj`` file
|
|
+
|
|
+ This method saves the object and texture to separate files in
|
|
+ a temporary directory and returns the object file name. This
|
|
+ is often used to launch a 3d viewer.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String. The file name (absolute path) of the saved obj file.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputSceneWavefront
|
|
+ sage: rich_output = OutputSceneWavefront.example(); rich_output
|
|
+ OutputSceneWavefront container
|
|
+ sage: obj = rich_output.obj_filename(); obj
|
|
+ '/.../scene.obj'
|
|
+ sage: print(open(obj).read())
|
|
+ mtllib scene.mtl
|
|
+ g obj_1
|
|
+ ...
|
|
+ f 3 2 6 8
|
|
+
|
|
+ sage: path = os.path.dirname(obj)
|
|
+ sage: mtl = os.path.join(path, 'scene.mtl'); mtl
|
|
+ '/.../scene.mtl'
|
|
+ sage: os.path.exists(mtl)
|
|
+ True
|
|
+ sage: os.path.dirname(obj) == os.path.dirname(mtl)
|
|
+ True
|
|
+ sage: print(open(mtl).read())
|
|
+ newmtl texture177
|
|
+ Ka 0.2 0.2 0.5
|
|
+ ...
|
|
+ d 1
|
|
+ """
|
|
+ from sage.misc.temporary_file import tmp_dir
|
|
+ basedir = tmp_dir()
|
|
+ obj_filename = os.path.join(basedir, 'scene.obj')
|
|
+ mtl_filename = os.path.join(basedir, self.mtllib())
|
|
+ self.obj.save_as(obj_filename)
|
|
+ self.mtl.save_as(mtl_filename)
|
|
+ return os.path.abspath(obj_filename)
|
|
+
|
|
+ @classmethod
|
|
+ def example(cls):
|
|
+ r"""
|
|
+ Construct a sample Canvas3D output container
|
|
+
|
|
+ This static method is meant for doctests, so they can easily
|
|
+ construct an example.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ An instance of :class:`OutputSceneCanvas3d`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.output_catalog import OutputSceneWavefront
|
|
+ sage: rich_output = OutputSceneWavefront.example(); rich_output
|
|
+ OutputSceneWavefront container
|
|
+
|
|
+ sage: rich_output.obj
|
|
+ buffer containing 227 bytes
|
|
+ sage: rich_output.obj.get()
|
|
+ 'mtllib scene.mtl\ng obj_1\n...\nf 1 5 6 2\nf 1 4 7 5\nf 6 5 7 8\nf 7 4 3 8\nf 3 2 6 8\n'
|
|
+
|
|
+ sage: rich_output.mtl
|
|
+ buffer containing 80 bytes
|
|
+ sage: rich_output.mtl.get()
|
|
+ 'newmtl texture177\nKa 0.2 0.2 0.5\nKd 0.4 0.4 1.0\nKs 0.0 0.0 0.0\nillum 1\nNs 1\nd 1\n'
|
|
+ """
|
|
+ from sage.env import SAGE_EXTCODE
|
|
+ with_path = lambda x: os.path.join(
|
|
+ SAGE_EXTCODE, 'doctest', 'rich_output', 'example_wavefront', x)
|
|
+ return cls(
|
|
+ OutputBuffer.from_file(with_path('scene.obj')),
|
|
+ OutputBuffer.from_file(with_path('scene.mtl')),
|
|
+ )
|
|
diff --git a/src/sage/repl/rich_output/preferences.py b/src/sage/repl/rich_output/preferences.py
|
|
new file mode 100644
|
|
index 0000000..8b8b442
|
|
--- /dev/null
|
|
+++ b/src/sage/repl/rich_output/preferences.py
|
|
@@ -0,0 +1,413 @@
|
|
+# -*- encoding: utf-8 -*-
|
|
+r"""
|
|
+Display Preferences
|
|
+
|
|
+This class is used to express display preferences that are not simply
|
|
+a choice of a particular output format. For example, whether to prefer
|
|
+vector over raster graphics. By convention, the value ``None`` is
|
|
+always a valid value for a preference and means no particular
|
|
+preference.
|
|
+
|
|
+EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.preferences import DisplayPreferences
|
|
+ sage: prefs = DisplayPreferences()
|
|
+ sage: prefs.available_options()
|
|
+ (graphics, text)
|
|
+ sage: prefs.text is None
|
|
+ True
|
|
+ sage: prefs.text = 'ascii_art'
|
|
+ sage: prefs.text
|
|
+ 'ascii_art'
|
|
+ sage: prefs
|
|
+ Display preferences:
|
|
+ * graphics is not specified
|
|
+ * text = ascii_art
|
|
+
|
|
+Properties can be unset by deleting them or by assinging ``None``::
|
|
+
|
|
+ sage: prefs.text = 'ascii_art'
|
|
+ sage: del prefs.text
|
|
+ sage: prefs.text is None
|
|
+ True
|
|
+
|
|
+ sage: prefs.text = 'ascii_art'
|
|
+ sage: prefs.text = None
|
|
+ sage: prefs.text is None
|
|
+ True
|
|
+
|
|
+Properties have documentation attached::
|
|
+
|
|
+ sage: import pydoc
|
|
+ sage: doc = pydoc.render_doc(prefs)
|
|
+ sage: assert ' graphics' in doc
|
|
+ sage: assert ' Preferred graphics format' in doc
|
|
+ sage: assert ' text' in doc
|
|
+ sage: assert ' Which textual representation is preferred' in doc
|
|
+
|
|
+Values can also be specified as keyword arguments to the constructor::
|
|
+
|
|
+ sage: DisplayPreferences(text='latex')
|
|
+ Display preferences:
|
|
+ * graphics is not specified
|
|
+ * text = latex
|
|
+
|
|
+.. TODO::
|
|
+
|
|
+ A value-checking preference system should be used elsewhere in
|
|
+ Sage, too. The class here is just a simple implementation, a
|
|
+ proper implementation would use a metaclass to construct the
|
|
+ preference items.
|
|
+"""
|
|
+
|
|
+#*****************************************************************************
|
|
+# Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com>
|
|
+#
|
|
+# Distributed under the terms of the GNU General Public License (GPL)
|
|
+# as published by the Free Software Foundation; either version 2 of
|
|
+# the License, or (at your option) any later version.
|
|
+# http://www.gnu.org/licenses/
|
|
+#*****************************************************************************
|
|
+
|
|
+
|
|
+from textwrap import dedent
|
|
+
|
|
+from sage.structure.sage_object import SageObject
|
|
+
|
|
+
|
|
+class Property(property):
|
|
+
|
|
+ def __init__(self, name, allowed_values, doc=None):
|
|
+ r"""
|
|
+ Preference item
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``name`` -- string. The name of the property.
|
|
+
|
|
+ - ``allowed_values`` -- list/tuple/iterable of allowed values.
|
|
+
|
|
+ - ``doc`` -- string (optional). The docstring of the property.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.preferences import Property
|
|
+ sage: prop = Property('foo', [0, 1, 2], 'The Foo Property')
|
|
+ sage: prop.__doc__
|
|
+ 'The Foo Property\n\nAllowed values:\n\n* ``None`` (default): no preference\n\n* 0\n\n* 1\n\n* 2'
|
|
+ sage: prop.allowed_values
|
|
+ (0, 1, 2)
|
|
+ """
|
|
+ self.name = name
|
|
+ self.underscore_name = '_{0}'.format(name)
|
|
+ self.allowed_values = tuple(allowed_values)
|
|
+ self.__doc__ = doc = self._make_doc(doc)
|
|
+ super(Property, self).__init__(
|
|
+ fget=self.getter, fset=self.setter, fdel=self.deleter, doc=doc)
|
|
+
|
|
+ def _make_doc(self, doc):
|
|
+ """
|
|
+ Generate the documentation.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``doc`` -- the title line of the documentation.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String. The docstring with auto-generated documentation about
|
|
+ the allowed values added.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.preferences import Property
|
|
+ sage: prop = Property('foo', [0, 1, 2], 'The Foo Property')
|
|
+ sage: print(prop._make_doc('this is the title'))
|
|
+ this is the title
|
|
+ <BLANKLINE>
|
|
+ Allowed values:
|
|
+ <BLANKLINE>
|
|
+ * ``None`` (default): no preference
|
|
+ <BLANKLINE>
|
|
+ * 0
|
|
+ <BLANKLINE>
|
|
+ * 1
|
|
+ <BLANKLINE>
|
|
+ * 2
|
|
+ """
|
|
+ doc = dedent(doc)
|
|
+ doc += '\n\n'
|
|
+ doc += 'Allowed values:\n\n'
|
|
+ values_doc = []
|
|
+ values_doc.append('* ``None`` (default): no preference')
|
|
+ for value in self.allowed_values:
|
|
+ values_doc.append('* {0}'.format(repr(value)))
|
|
+ return doc + '\n\n'.join(values_doc)
|
|
+
|
|
+ def __repr__(self):
|
|
+ """
|
|
+ Return a string representation
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.preferences import Property
|
|
+ sage: prop = Property('foo', [0, 1, 2], 'The Foo Property')
|
|
+ sage: prop.__repr__()
|
|
+ 'foo'
|
|
+ """
|
|
+ return self.name
|
|
+
|
|
+ def getter(self, prefs):
|
|
+ """
|
|
+ Get the current value of the property
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``prefs`` -- the :class:`PreferencesABC` instance that the
|
|
+ property is bound to.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ One of the allowed values or ``None`` if not set.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.preferences import Property, PreferencesABC
|
|
+ sage: prop = Property('foo', [0, 1, 2], 'The Foo Property')
|
|
+ sage: prefs = PreferencesABC()
|
|
+ sage: prop.getter(prefs) is None
|
|
+ True
|
|
+ sage: prop.setter(prefs, 1)
|
|
+ sage: prop.getter(prefs)
|
|
+ 1
|
|
+ """
|
|
+ try:
|
|
+ return getattr(prefs, self.underscore_name)
|
|
+ except AttributeError:
|
|
+ return None
|
|
+
|
|
+ def setter(self, prefs, value):
|
|
+ """
|
|
+ Get the current value of the property
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``prefs`` -- the :class:`PreferencesABC` instance that the
|
|
+ property is bound to.
|
|
+
|
|
+ - ``value`` -- anything. The new value of the
|
|
+ property. Setting a property to ``None`` is equivalent to
|
|
+ deleting the value.
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ This method does not return anything. A ``ValueError`` is
|
|
+ raised if the given ``value`` is not one of the allowed
|
|
+ values.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.preferences import Property, PreferencesABC
|
|
+ sage: prop = Property('foo', [0, 1, 2], 'The Foo Property')
|
|
+ sage: prefs = PreferencesABC()
|
|
+ sage: prop.getter(prefs) is None
|
|
+ True
|
|
+ sage: prop.setter(prefs, 1)
|
|
+ sage: prop.getter(prefs)
|
|
+ 1
|
|
+
|
|
+ sage: prop.setter(prefs, None)
|
|
+ sage: prop.getter(prefs) is None
|
|
+ True
|
|
+ """
|
|
+ if value is None:
|
|
+ return self.deleter(prefs)
|
|
+ allowed = self.allowed_values
|
|
+ if value not in allowed:
|
|
+ raise ValueError('value must be unset (None) or one of {0}, got {1}'
|
|
+ .format(allowed, value))
|
|
+ setattr(prefs, self.underscore_name, value)
|
|
+
|
|
+ def deleter(self, prefs):
|
|
+ """
|
|
+ Delete the current value of the property
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``prefs`` -- the :class:`PreferencesABC` instance that the
|
|
+ property is bound to.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.preferences import Property, PreferencesABC
|
|
+ sage: prop = Property('foo', [0, 1, 2], 'The Foo Property')
|
|
+ sage: prefs = PreferencesABC()
|
|
+ sage: prop.getter(prefs) is None
|
|
+ True
|
|
+ sage: prop.setter(prefs, 1)
|
|
+ sage: prop.deleter(prefs)
|
|
+ sage: prop.getter(prefs) is None
|
|
+ True
|
|
+ """
|
|
+ underscore_name = self.underscore_name
|
|
+ try:
|
|
+ delattr(prefs, underscore_name)
|
|
+ except AttributeError:
|
|
+ pass
|
|
+
|
|
+
|
|
+class PreferencesABC(SageObject):
|
|
+
|
|
+ def __init__(self, *args, **kwds):
|
|
+ """
|
|
+ Preferences for displaying graphics
|
|
+
|
|
+ These can be preferences expressed by the user or by the
|
|
+ display backend. They are specified as keyword arguments.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``*args*`` -- positional arguments are preferences
|
|
+ instances. The property values will be inherited from left
|
|
+ to right, that is, later parents override values from
|
|
+ earlier parents.
|
|
+
|
|
+ - ``**kwds`` -- keyword arguments. Will be used to initialize
|
|
+ properties, and override inherited values if necessary.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.preferences import DisplayPreferences
|
|
+ sage: p1 = DisplayPreferences(graphics='vector')
|
|
+ sage: p2 = DisplayPreferences(graphics='raster')
|
|
+ sage: DisplayPreferences(p1, p2)
|
|
+ Display preferences:
|
|
+ * graphics = raster
|
|
+ * text is not specified
|
|
+
|
|
+ If specified in the opposite order, the setting from ``p1`` is
|
|
+ inherited::
|
|
+
|
|
+ sage: DisplayPreferences(p2, p1)
|
|
+ Display preferences:
|
|
+ * graphics = vector
|
|
+ * text is not specified
|
|
+
|
|
+ Further keywords override::
|
|
+
|
|
+ sage: DisplayPreferences(p2, p1, graphics='disable')
|
|
+ Display preferences:
|
|
+ * graphics = disable
|
|
+ * text is not specified
|
|
+ """
|
|
+ for parent in args:
|
|
+ for option in self.available_options():
|
|
+ value = option.getter(parent)
|
|
+ if value is not None:
|
|
+ option.setter(self, value)
|
|
+ for key, value in kwds.items():
|
|
+ setattr(self, key, value)
|
|
+
|
|
+ @classmethod
|
|
+ def _add_option(cls, name, values, doc):
|
|
+ """
|
|
+ Add an option to the preferences system.
|
|
+
|
|
+ This method should only be called during the import of
|
|
+ :mod:`sage.repl.rich_output.preferences`.
|
|
+
|
|
+ INPUT:
|
|
+
|
|
+ - ``name`` -- the name of the option.
|
|
+
|
|
+ - ``values`` -- the allowed values.
|
|
+
|
|
+ - ``doc`` -- docstring.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.preferences import PreferencesABC
|
|
+ sage: class MyPrefs(PreferencesABC):
|
|
+ ....: pass
|
|
+ sage: MyPrefs._add_option('foo', [0, 1, 2], 'The Foo Option')
|
|
+ sage: prefs = MyPrefs()
|
|
+ sage: prefs.foo
|
|
+ sage: prefs.foo = 0
|
|
+ sage: prefs.foo
|
|
+ 0
|
|
+ """
|
|
+ prop = Property(name, values, doc)
|
|
+ setattr(cls, name, prop)
|
|
+
|
|
+ def available_options(self):
|
|
+ """
|
|
+ Return the available options
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ Tuple of the preference items as instances of
|
|
+ :class:`Property`.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.preferences import DisplayPreferences
|
|
+ sage: DisplayPreferences().available_options()
|
|
+ (graphics, text)
|
|
+ """
|
|
+ options = []
|
|
+ for key, value in self.__class__.__dict__.items():
|
|
+ if isinstance(value, Property):
|
|
+ options.append(value)
|
|
+ return tuple(sorted(options, key=str))
|
|
+
|
|
+ def _repr_(self):
|
|
+ r"""
|
|
+ Return a string representation
|
|
+
|
|
+ OUTPUT:
|
|
+
|
|
+ String.
|
|
+
|
|
+ EXAMPLES::
|
|
+
|
|
+ sage: from sage.repl.rich_output.preferences import DisplayPreferences
|
|
+ sage: DisplayPreferences()._repr_()
|
|
+ 'Display preferences:\n* graphics is not specified\n* text is not specified'
|
|
+ """
|
|
+ s = ['Display preferences:']
|
|
+ for opt in self.available_options():
|
|
+ value = opt.getter(self)
|
|
+ if value is None:
|
|
+ s += ['* {0} is not specified'.format(opt.name)]
|
|
+ else:
|
|
+ s += ['* {0} = {1}'.format(opt.name, value)]
|
|
+ return '\n'.join(s)
|
|
+
|
|
+
|
|
+class DisplayPreferences(PreferencesABC):
|
|
+ pass
|
|
+
|
|
+
|
|
+DisplayPreferences._add_option(
|
|
+ 'text',
|
|
+ ('plain', 'ascii_art', 'latex'),
|
|
+ """
|
|
+ Which textual representation is preferred
|
|
+ """
|
|
+)
|
|
+
|
|
+
|
|
+DisplayPreferences._add_option(
|
|
+ 'graphics',
|
|
+ ('disable', 'vector', 'raster'),
|
|
+ """
|
|
+ Preferred graphics format
|
|
+ """
|
|
+)
|
|
+
|
|
+
|
|
+
|
|
diff --git a/src/sage/repl/zmq_kernel.py b/src/sage/repl/zmq_kernel.py
|
|
deleted file mode 100644
|
|
index e5f77c3..0000000
|
|
--- a/src/sage/repl/zmq_kernel.py
|
|
+++ /dev/null
|
|
@@ -1,122 +0,0 @@
|
|
-"""
|
|
-The Sage ZMQ Kernel
|
|
-
|
|
-Version of the IPython kernel when running Sage inside the IPython
|
|
-notebook or remote IPython sessions.
|
|
-"""
|
|
-
|
|
-from IPython.kernel.zmq.ipkernel import Kernel
|
|
-from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
|
|
-from IPython.utils.traitlets import Type
|
|
-from IPython.core.formatters import DisplayFormatter
|
|
-
|
|
-from sage.structure.graphics_file import Mime
|
|
-from sage.repl.interpreter import SageInteractiveShell
|
|
-from sage.repl.display.formatter import SagePlainTextFormatter
|
|
-from sage.misc.temporary_file import tmp_filename
|
|
-from sage.structure.sage_object import SageObject
|
|
-
|
|
-
|
|
-class SageZMQDisplayFormatter(DisplayFormatter):
|
|
-
|
|
- def __init__(self, *args, **kwds):
|
|
- """
|
|
- Override for IPython's display formatter class
|
|
-
|
|
- IPython always creates all representations of an object, and
|
|
- the frontend then picks which one it displays. This would be
|
|
- rather ineffective for Sage, where we have numerous output
|
|
- graphics formats. We want to only create the most suitable
|
|
- output representation, and not png/jpg/pdf/svg/... versions
|
|
- for each image.
|
|
- """
|
|
- shell = kwds['parent']
|
|
- self.plain_text = SagePlainTextFormatter(config=shell.config)
|
|
-
|
|
- _format_types = frozenset([
|
|
- Mime.TEXT,
|
|
- Mime.HTML,
|
|
- Mime.LATEX,
|
|
- Mime.JSON,
|
|
- Mime.JAVASCRIPT,
|
|
- Mime.PDF,
|
|
- Mime.PNG,
|
|
- Mime.JPG,
|
|
- Mime.SVG,
|
|
- ])
|
|
-
|
|
- @property
|
|
- def format_types(self):
|
|
- """
|
|
- Return the enabled format types (MIME types)
|
|
-
|
|
- OUTPUT:
|
|
-
|
|
- Set of mime types (as strings).
|
|
-
|
|
- EXAMPLES::
|
|
-
|
|
- sage: from sage.repl.zmq_kernel import SageZMQDisplayFormatter
|
|
- sage: from sage.repl.interpreter import get_test_shell
|
|
- sage: fmt = SageZMQDisplayFormatter(parent=get_test_shell())
|
|
- sage: fmt.format_types
|
|
- frozenset({u'application/javascript',
|
|
- u'application/json',
|
|
- u'application/pdf',
|
|
- u'image/jpeg',
|
|
- u'image/png',
|
|
- u'image/svg+xml',
|
|
- u'text/html',
|
|
- u'text/latex',
|
|
- u'text/plain'})
|
|
- """
|
|
- return self._format_types
|
|
-
|
|
- # TODO: setter for format_types?
|
|
-
|
|
- def format(self, obj, include=None, exclude=None):
|
|
- """
|
|
- Return a format data dict for an object
|
|
-
|
|
- INPUT:
|
|
-
|
|
- - ``obj`` -- anything. The object to represent.
|
|
-
|
|
- - ``include``, ``exclude`` -- IPython mime types to
|
|
- include/exclude. (currently ignored)
|
|
-
|
|
- OUTPUT:
|
|
-
|
|
- A pair consisting of the representation dictionary and the metadata dictionary.
|
|
-
|
|
- EXAMPLES::
|
|
-
|
|
- sage: from sage.repl.zmq_kernel import SageZMQDisplayFormatter
|
|
- sage: from sage.repl.interpreter import get_test_shell
|
|
- sage: fmt = SageZMQDisplayFormatter(parent=get_test_shell())
|
|
- sage: fmt.format(123)
|
|
- ({u'text/plain': '123'}, {})
|
|
- """
|
|
- output = dict()
|
|
- if isinstance(obj, SageObject) and hasattr(obj, '_graphics_'):
|
|
- gfx = obj._graphics_(mime_types=self.format_types)
|
|
- if gfx is not None:
|
|
- output[gfx.mime()] = gfx.data()
|
|
- if Mime.TEXT not in output:
|
|
- output[Mime.TEXT] = self.plain_text(obj)
|
|
- return (output, {})
|
|
-
|
|
-
|
|
-class SageZMQInteractiveShell(SageInteractiveShell, ZMQInteractiveShell):
|
|
-
|
|
- def init_display_formatter(self):
|
|
- """
|
|
- Use our display formatter instead of the IPython default.
|
|
- """
|
|
- self.display_formatter = SageZMQDisplayFormatter(parent=self)
|
|
- self.configurables.append(self.display_formatter)
|
|
-
|
|
-
|
|
-class SageKernel(Kernel):
|
|
- shell_class = Type(SageZMQInteractiveShell)
|
|
-
|
|
diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx
|
|
index a5506b7..12ba423 100644
|
|
--- a/src/sage/structure/sage_object.pyx
|
|
+++ b/src/sage/structure/sage_object.pyx
|
|
@@ -211,8 +211,9 @@ cdef class SageObject:
|
|
+---+---+
|
|
| 1 | 2 |
|
|
+---+---+
|
|
- sage: shell.run_cell('%display simple')
|
|
+ sage: shell.run_cell('%display plain')
|
|
sage: shell.run_cell('Tableaux.global_options.reset()')
|
|
+ sage: shell.quit()
|
|
|
|
TESTS::
|