{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# What to Expect When You are Expecting a New Hutch Python\n", "\n", "The purpose of this tutorial is to provide a general overview of the new `hutch_python` system. Underneath the code is built on top of [ophyd](http://nsls-ii.github.io/ophyd) and [bluesky](https://nsls-ii.github.io/bluesky) as well as many **PCDS** specific libraries. If you are looking for more in depth information on a specific part of the control system documentation is hosted on https://pcdshub.github.io.\n", "\n", "## Table of Contents\n", "\n", "* [Navigation](#Navigation)\n", "* [How Devices Are Loaded](#How-Devices-Are-Loaded)\n", "* [An Introduction to Scanning](#An-Introduction-to-Scanning)\n", "\n", "If you do not want to setup a Jupyter Notebook environment yourself, skip ahead to [navigation](#Navigation). The code below is executed automatically when you launch the hutch python environment at a hutch." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "%matplotlib notebook\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "from hutch_python.load_conf import load\n", "from hutch_python.log_setup import setup_logging, debug_mode\n", "from pcdsdaq.sim import set_sim_mode as set_daq_sim\n", "from bluesky.utils import install_nb_kicker\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[38;5;202m __ _____ _ _ \n", " / _| | __ \\ | | | | \n", " _ __ ___ | |___ _| |__) | _| |_| |__ ___ _ __ \n", "| '_ ` _ \\| _\\ \\/ / ___/ | | | __| '_ \\ / _ \\| '_ \\ \n", "| | | | | | | > <| | | |_| | |_| | | | (_) | | | |\n", "|_| |_| |_|_| /_/\\_\\_| \\__, |\\__|_| |_|\\___/|_| |_|\n", " __/ | \n", " |___/ \n", "\n", "\u001b[1;30mINFO \u001b[0m Loading daq...\n", "\u001b[1;30mSUCCESS \u001b[0m \u001b[1;32mSuccessfully loaded daq\u001b[0m\n", "\u001b[1;30mINFO \u001b[0m Loading database...\n", "\u001b[1;30mINFO \u001b[0m Loading mfx_mxt_valve [pcdsdevices.device_types.GateValve] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading mfx_attenuator [pcdsdevices.device_types.Attenuator] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading mfx_dg1_slits [pcdsdevices.device_types.Slits] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading mfx_dg1_pim [pcdsdevices.device_types.PIM] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading mfx_dg1_valve_1 [pcdsdevices.device_types.GateValve] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading mfx_dg1_valve_2 [pcdsdevices.device_types.GateValve] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading mfx_dg2_downstream_slits [pcdsdevices.device_types.Slits] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading mfx_dg2_midstream_slits [pcdsdevices.device_types.Slits] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading mfx_dg2_upstream_slits [pcdsdevices.device_types.Slits] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading mfx_dg2_pim [pcdsdevices.device_types.PIM] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading mfx_pulsepicker [pcdsdevices.device_types.PulsePicker] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading mfx_dia_valve [pcdsdevices.device_types.GateValve] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading mfx_dg2_valve [pcdsdevices.device_types.GateValve] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading mfx_dvd_valve [pcdsdevices.device_types.GateValve] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading mfx_dia_ipm [pcdsdevices.device_types.IPM] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading mfx_dia_pim [pcdsdevices.pim.PIMMotor] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading mfx_reflaser [pcdsdevices.inout.InOutRecordPositioner] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading sh45 [pcdsdevices.valve.PPSStopper] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mSUCCESS \u001b[0m \u001b[1;32mSuccessfully loaded database\u001b[0m\n", "\u001b[1;30mINFO \u001b[0m Loading mfx.beamline...\n", "\u001b[1;30mINFO \u001b[0m Loading transfocator...\n", "\u001b[1;30mSUCCESS \u001b[0m \u001b[1;32mSuccessfully loaded transfocator\u001b[0m\n", "\u001b[1;30mINFO \u001b[0m Loading mfx_prefocus...\n", "\u001b[1;30mSUCCESS \u001b[0m \u001b[1;32mSuccessfully loaded mfx_prefocus\u001b[0m\n", "\u001b[1;30mINFO \u001b[0m Loading beam_suspender...\n", "\u001b[1;30mSUCCESS \u001b[0m \u001b[1;32mSuccessfully loaded beam_suspender\u001b[0m\n", "\u001b[1;30mSUCCESS \u001b[0m \u001b[1;32mSuccessfully loaded mfx.beamline\u001b[0m\n", "\u001b[1;30mINFO \u001b[0m Loading questionnaire...\n", "\u001b[1;30mWARNING \u001b[0m \u001b[33mUnable to create an object from Questionnaire table motors row 10\u001b[0m\n", "\u001b[1;30mWARNING \u001b[0m \u001b[33mUnable to create an object from Questionnaire table motors row 11\u001b[0m\n", "\u001b[1;30mINFO \u001b[0m Loading laser_waveplate [pcdsdevices.epics_motor.Newport] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading inj_x [pcdsdevices.epics_motor.Newport] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading inj_y [pcdsdevices.epics_motor.Newport] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading shield_y [pcdsdevices.epics_motor.Newport] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading shield_x [pcdsdevices.epics_motor.Newport] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading off_nav_focus [pcdsdevices.epics_motor.IMS] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading off_nav_zoom [pcdsdevices.epics_motor.IMS] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading tilt [pcdsdevices.epics_motor.Newport] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading tip [pcdsdevices.epics_motor.Newport] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading onAxis_Y [pcdsdevices.epics_motor.Newport] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading onAxis_Z [pcdsdevices.epics_motor.Newport] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading offAxis_Y [pcdsdevices.epics_motor.Newport] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading offAxis_Z [pcdsdevices.epics_motor.Newport] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading platform_Z [pcdsdevices.epics_motor.Newport] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading in_nav_zoom [pcdsdevices.epics_motor.IMS] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mINFO \u001b[0m Loading in_nav_focus [pcdsdevices.epics_motor.IMS] ... \u001b[32mSUCCESS\u001b[0m!\n", "\u001b[1;30mSUCCESS \u001b[0m \u001b[1;32mSuccessfully loaded questionnaire\u001b[0m\n", "\u001b[1;30mINFO \u001b[0m Loading ls2116...\n", "\u001b[1;30mSUCCESS \u001b[0m \u001b[1;32mSuccessfully loaded ls2116\u001b[0m\n", "\u001b[1;30mINFO \u001b[0m Loading default groups...\n", "\u001b[1;30mSUCCESS \u001b[0m \u001b[1;32mSuccessfully loaded default groups\u001b[0m\n" ] } ], "source": [ "setup_logging(dir_logs='logs')\n", "install_nb_kicker()\n", "set_daq_sim(True)\n", "globals().update(load('conf.yml'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Navigation\n", "\n", "At the beginning of your session, the entire beamline is loaded into the global namespace. That means that any device you see printed in the opening banner is accessible\n", " \n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "False\n" ] } ], "source": [ "print(mfx_dg1_pim.inserted)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In addition, devices are sorted into different namespaces based on their class and position along the beamline. This can be a useful way to find devices that you may not know the exact name for. These groups are useful as all members are tag accessible and you can also iterate through the devices contained for quick off the cuff scripts." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "mfx_dg1_slits (2.9994, 3.0000999999999998)\n", "mfx_dg2_downstream_slits (2.9999000000000007, 2.9999999999999996)\n", "mfx_dg2_midstream_slits (2.9999000000000002, 2.9994000000000005)\n", "mfx_dg2_upstream_slits (2.9999000000000002, 2.9997000000000003)\n" ] } ], "source": [ "for slit in slits:\n", " print(slit.name, slit.position)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "mfx_dg1_pim\n", "mfx_reflaser\n", "mfx_dg1_slits\n", "mfx_dg1_valve_1\n", "mfx_dg1_valve_2\n" ] } ], "source": [ "for device in mfx.dg1:\n", " print(device.name)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are a few utilities if you don't see a grouping you like, `class_namespace`, `tree_namespace` allow you to create these groupings quickly based on information about a device." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "mfx_dg1_valve_2 OUT\n", "mfx_dg1_valve_1 OUT\n", "mfx_dg1_valve_2 OUT\n", "mfx_dg2_valve OUT\n", "mfx_dia_valve OUT\n", "mfx_dvd_valve OUT\n", "mfx_mxt_valve OUT\n" ] } ], "source": [ "from pcdsdevices.device_types import GateValve\n", "from hutch_python.namespace import class_namespace\n", "\n", "valves = class_namespace(GateValve)\n", "for valve in valves:\n", " print(valve.name, valve.position)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Lightpath\n", "\n", "Included in each hutch python environment is a `BeamPath`, saved under the name `hutch_beampth`, that gives a somewhat more physical mapping of the beamline. You can also use this to access specific devices if you find it more intuitive. If you are curious about more of the lightpath feature set more documentation can be found at https://pcdshub.github.io/lightpath" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "+--------------------------+------------------------+------------+----------+---------+\n", "| Name | Prefix | Position | Beamline | Removed |\n", "+--------------------------+------------------------+------------+----------+---------+\n", "| fee_m1h | FEE1:M1H | 740.51000 | HXD | False |\n", "| fee_m2h | FEE1:M2H | 751.84300 | HXD | False |\n", "| sh1 | PPS:NEH1:1:SH1INSUM | 755.42100 | HXD | True |\n", "| uvd_valve | HX2:UVD:VGC:01 | 769.00000 | HXD | True |\n", "| hx2_reference_laser | HX2:REFLASER:MIRROR | 773.50500 | HXD | True |\n", "| hx2_slits | HX2:SB1:JAWS | 773.60500 | HXD | False |\n", "| hx2_ipm | HX2:SB1:IPM | 773.70500 | HXD | True |\n", "| hx2_pim | HX2:SB1:PIM | 773.80500 | HXD | True |\n", "| hx2_valve | HX2:DVD:VGC:01 | 779.00000 | HXD | True |\n", "| xpp_lodcm | XPP:LODCM | 781.10000 | HXD | True |\n", "| hxd_mon_valve_1 | HX3:MON:VGC:01 | 782.50000 | HXD | True |\n", "| hxd_mon_valve_2 | HX3:MON:VGC:02 | 783.50000 | HXD | True |\n", "| sh2 | STPR:XRT1:1:SH2_PPSSUM | 803.10000 | HXD | False |\n", "| um6_slits | HXX:UM6:JAWS | 809.96600 | HXD | True |\n", "| um6_pim | HXX:UM6:PIM | 810.13500 | HXD | True |\n", "| um6_ipm | HXX:UM6:IPM | 810.33200 | HXD | True |\n", "| um6_stopper | HXX:UM6:STP:01 | 811.03500 | HXD | True |\n", "| mxt_valve_1 | HXX:MXT:VGC:01 | 812.86900 | HXD | True |\n", "| xrt_m1h | XRT:M1H | 814.71600 | HXD | False |\n", "| xrt_m2h | XRT:M2H | 817.11600 | HXD | False |\n", "| mfx_mxt_valve | HXX:MXT:VGC:04 | 870.96000 | MFX | True |\n", "| mfx_dvd_valve | MFX:DVD:VGC:01 | 945.96300 | MFX | True |\n", "| sh45 | PPS:FEH1:45:S45STPRSUM | 952.20000 | MFX | False |\n", "| mfx_dia_valve | MFX:DIA:VGC:01 | 983.00000 | MFX | True |\n", "| mfx_pulsepicker | MFX:DIA:MMS:07 | 984.20000 | MFX | False |\n", "| mfx_attenuator | MFX:ATT | 984.40000 | MFX | False |\n", "| mfx_dia_ipm | MFX:IPM1 | 984.85000 | MFX | False |\n", "| mfx_dia_pim | MFX:PIM1 | 984.90000 | MFX | True |\n", "| mfx_dg2_valve | MFX:DIA:VGC:02 | 990.10000 | MFX | True |\n", "| mfx_dg1_valve_1 | MFX:DG1:VGC:01 | 1018.54000 | MFX | True |\n", "| mfx_reflaser | MFX:REFLASER:MIRROR | 1018.82000 | MFX | False |\n", "| mfx_dg1_slits | MFX:DG1:JAWS | 1019.30000 | MFX | False |\n", "| mfx_dg1_pim | MFX:DG1:PIM | 1019.79000 | MFX | True |\n", "| mfx_dg1_valve_2 | MFX:DG1:VGC:02 | 1019.99000 | MFX | True |\n", "| mfx_dg2_upstream_slits | MFX:DG2:JAWS:US | 1021.29000 | MFX | False |\n", "| mfx_dg2_pim | MFX:DG2:PIM | 1021.74000 | MFX | False |\n", "| mfx_dg2_midstream_slits | MFX:DG2:JAWS:MS | 1022.84000 | MFX | False |\n", "| mfx_dg2_downstream_slits | MFX:DG2:JAWS:DS | 1022.99000 | MFX | False |\n", "+--------------------------+------------------------+------------+----------+---------+\n" ] } ], "source": [ "mfx_beampath.show_devices()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Devices included in the lightpath should all share a common API for accessing and moving between states. This includes `.position`, `.inserted`, `.removed`, and methods `.insert()`, `.remove()` " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### DAQ" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Last but not least is the DAQ object which is instantiated in each session but must be connected by the user. The DAQ can only be controlled externally by a single session and the Python session must be running on the same machine as the DAQ." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[1;30mINFO \u001b[0m Connected to DAQ\n", "\u001b[1;30mINFO \u001b[0m Daq configured\n" ] } ], "source": [ "daq.connect()\n", "daq.begin(events=10)\n", "daq.end_run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## How Devices Are Loaded\n", "\n", "Individual EPICS devices can come three different locations within the `hutch_python` ecosystem; the permenant `happi` device database, the LCLS User Questionnaire, and the `mfx` repository itself. By watching the loading banner itself you should be able to tell which device came from where. In general, static beamline instrumentation should be held within the `happi` database. This gives us a stable understanding of most of the beamline devices and will also be used to inform other user interfaces and applications in the future.\n", "\n", "Included is information on where the device is, what the prefix of the camera is e.t.c. Devices loaded this way will have an attribute `.md` that provides a helpful link to the database entry behind the device. " ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "+--------------+------------------------------------------------------+\n", "| EntryInfo | Value |\n", "+--------------+------------------------------------------------------+\n", "| active | True |\n", "| args | ['{{prefix}}'] |\n", "| beamline | MFX |\n", "| creation | Mon Jan 29 14:04:28 2018 |\n", "| data | None |\n", "| device_class | pcdsdevices.device_types.PIM |\n", "| kwargs | {'name': '{{name}}', 'prefix_det': '{{prefix_det}}'} |\n", "| last_edit | Tue Feb 27 10:21:22 2018 |\n", "| macros | None |\n", "| name | mfx_dg1_pim |\n", "| parent | None |\n", "| prefix | MFX:DG1:PIM |\n", "| prefix_det | MFX:DG1:P6740 |\n", "| screen | None |\n", "| stand | DG1 |\n", "| system | diagnostic |\n", "| type | PIM |\n", "| z | 1019.79000 |\n", "+--------------+------------------------------------------------------+\n" ] } ], "source": [ "mfx_dg1_pim.md.show_info()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The LCLS Questionnaire will produce similar looking devices. The difference is that these are loaded directly from the information contained on the PCDS setup page. This means if you would like to change any of these devices simply update the appropriate table and reload your session. The proposal number is retrieved automatically but if you would like to force the configuration to use a different proposal's setup simply specify it in the `conf.yaml`\n", "\n", "```YAML\n", "experiment:\n", " proposal: LS21\n", " run: 16\n", "```\n" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "+--------------+---------------------------------+\n", "| EntryInfo | Value |\n", "+--------------+---------------------------------+\n", "| active | True |\n", "| args | ['{{prefix}}'] |\n", "| beamline | MFX |\n", "| device_class | pcdsdevices.epics_motor.Newport |\n", "| kwargs | {'name': '{{name}}'} |\n", "| location | Hutch-main experimental |\n", "| macros | None |\n", "| name | shield_x |\n", "| parent | None |\n", "| prefix | MFX:USR:MMN:32 |\n", "| purpose | X-ray Shield Tube X |\n", "| screen | None |\n", "| stand | None |\n", "| system | None |\n", "| type | Device |\n", "| z | -1.00000 |\n", "+--------------+---------------------------------+\n" ] } ], "source": [ "shield_x.md.show_info()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now if devices aren't loaded through either the Questionnaire or the device database itself, the old model of placing objects in an experiment specific file or the `hutch/beamline.py` file are still supported. These maybe device types that are still under development and have not been pushed upstream e.t.c" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## An Introduction to Scanning\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "One of the major additions the `hutch_python` restructuring gives us is access to `bluesky` scanning capabilities. A full tutorial is available at http://nsls-ii.github.io/bluesky/tutorial. This very brief introduction assumes you know a little about how `bluesky` works in general. The major keys are:\n", "\n", "* The `RunEngine` object is responsible for executing all scans. \n", "* Experimental procedures are described in `plans`, python generators which allow sophisticated flow control\n", "\n", "\n", "The hutch-python environment should already have a `RunEngine` instatiated for you to begin playing.\n", "It also has built-in `bluesky` `plans` that are tab-accessible in the following objects:\n", "\n", "* `bp`: full plans that are ready to use\n", "* `bps`: partial plans that can be used as building blocks for larger plans\n", "* `bpp`: wrappers that add functionality to existing plans\n", "\n", "\n", "#### Note\n", "In the examples below we run our scans with simulated hardware built-in to the `ophyd` library.\n" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "from ophyd.sim import motor, det" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The simplest plan in `bluesky` is `count`, a simple reading of a detector. A basic execution looks like this where we take 5 measurements from the detector. Take note what is actually happenning here. We create an instance of `count` then we pass this into `RE()`. " ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Transient Scan ID: 1 Time: 2018/03/14 09:18:37\n", "Persistent Unique Scan ID: 'dda5fba6-989a-4069-9aa3-d40415534e4c'\n", "New stream: 'primary'\n", "+-----------+------------+------------+\n", "| seq_num | time | det |\n", "+-----------+------------+------------+\n", "| 1 | 09:18:37.9 | 1.000 |\n", "| 2 | 09:18:38.1 | 1.000 |\n", "| 3 | 09:18:38.1 | 1.000 |\n", "| 4 | 09:18:38.1 | 1.000 |\n", "| 5 | 09:18:38.1 | 1.000 |\n", "+-----------+------------+------------+\n", "generator count ['dda5fba6'] (scan num: 1)\n", "\n", "\n", "\n" ] }, { "data": { "application/javascript": [ "/* Put everything inside the global mpl namespace */\n", "window.mpl = {};\n", "\n", "\n", "mpl.get_websocket_type = function() {\n", " if (typeof(WebSocket) !== 'undefined') {\n", " return WebSocket;\n", " } else if (typeof(MozWebSocket) !== 'undefined') {\n", " return MozWebSocket;\n", " } else {\n", " alert('Your browser does not have WebSocket support.' +\n", " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", " 'Firefox 4 and 5 are also supported but you ' +\n", " 'have to enable WebSockets in about:config.');\n", " };\n", "}\n", "\n", "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", " this.id = figure_id;\n", "\n", " this.ws = websocket;\n", "\n", " this.supports_binary = (this.ws.binaryType != undefined);\n", "\n", " if (!this.supports_binary) {\n", " var warnings = document.getElementById(\"mpl-warnings\");\n", " if (warnings) {\n", " warnings.style.display = 'block';\n", " warnings.textContent = (\n", " \"This browser does not support binary websocket messages. \" +\n", " \"Performance may be slow.\");\n", " }\n", " }\n", "\n", " this.imageObj = new Image();\n", "\n", " this.context = undefined;\n", " this.message = undefined;\n", " this.canvas = undefined;\n", " this.rubberband_canvas = undefined;\n", " this.rubberband_context = undefined;\n", " this.format_dropdown = undefined;\n", "\n", " this.image_mode = 'full';\n", "\n", " this.root = $('
');\n", " this._root_extra_style(this.root)\n", " this.root.attr('style', 'display: inline-block');\n", "\n", " $(parent_element).append(this.root);\n", "\n", " this._init_header(this);\n", " this._init_canvas(this);\n", " this._init_toolbar(this);\n", "\n", " var fig = this;\n", "\n", " this.waiting = false;\n", "\n", " this.ws.onopen = function () {\n", " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", " fig.send_message(\"send_image_mode\", {});\n", " if (mpl.ratio != 1) {\n", " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", " }\n", " fig.send_message(\"refresh\", {});\n", " }\n", "\n", " this.imageObj.onload = function() {\n", " if (fig.image_mode == 'full') {\n", " // Full images could contain transparency (where diff images\n", " // almost always do), so we need to clear the canvas so that\n", " // there is no ghosting.\n", " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", " }\n", " fig.context.drawImage(fig.imageObj, 0, 0);\n", " };\n", "\n", " this.imageObj.onunload = function() {\n", " fig.ws.close();\n", " }\n", "\n", " this.ws.onmessage = this._make_on_message_function(this);\n", "\n", " this.ondownload = ondownload;\n", "}\n", "\n", "mpl.figure.prototype._init_header = function() {\n", " var titlebar = $(\n", " '');\n", " var titletext = $(\n", " '');\n", " titlebar.append(titletext)\n", " this.root.append(titlebar);\n", " this.header = titletext[0];\n", "}\n", "\n", "\n", "\n", "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", "\n", "}\n", "\n", "\n", "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", "\n", "}\n", "\n", "mpl.figure.prototype._init_canvas = function() {\n", " var fig = this;\n", "\n", " var canvas_div = $('');\n", "\n", " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", "\n", " function canvas_keyboard_event(event) {\n", " return fig.key_event(event, event['data']);\n", " }\n", "\n", " canvas_div.keydown('key_press', canvas_keyboard_event);\n", " canvas_div.keyup('key_release', canvas_keyboard_event);\n", " this.canvas_div = canvas_div\n", " this._canvas_extra_style(canvas_div)\n", " this.root.append(canvas_div);\n", "\n", " var canvas = $('');\n", " canvas.addClass('mpl-canvas');\n", " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", "\n", " this.canvas = canvas[0];\n", " this.context = canvas[0].getContext(\"2d\");\n", "\n", " var backingStore = this.context.backingStorePixelRatio ||\n", "\tthis.context.webkitBackingStorePixelRatio ||\n", "\tthis.context.mozBackingStorePixelRatio ||\n", "\tthis.context.msBackingStorePixelRatio ||\n", "\tthis.context.oBackingStorePixelRatio ||\n", "\tthis.context.backingStorePixelRatio || 1;\n", "\n", " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", "\n", " var rubberband = $('');\n", " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", "\n", " var pass_mouse_events = true;\n", "\n", " canvas_div.resizable({\n", " start: function(event, ui) {\n", " pass_mouse_events = false;\n", " },\n", " resize: function(event, ui) {\n", " fig.request_resize(ui.size.width, ui.size.height);\n", " },\n", " stop: function(event, ui) {\n", " pass_mouse_events = true;\n", " fig.request_resize(ui.size.width, ui.size.height);\n", " },\n", " });\n", "\n", " function mouse_event_fn(event) {\n", " if (pass_mouse_events)\n", " return fig.mouse_event(event, event['data']);\n", " }\n", "\n", " rubberband.mousedown('button_press', mouse_event_fn);\n", " rubberband.mouseup('button_release', mouse_event_fn);\n", " // Throttle sequential mouse events to 1 every 20ms.\n", " rubberband.mousemove('motion_notify', mouse_event_fn);\n", "\n", " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", "\n", " canvas_div.on(\"wheel\", function (event) {\n", " event = event.originalEvent;\n", " event['data'] = 'scroll'\n", " if (event.deltaY < 0) {\n", " event.step = 1;\n", " } else {\n", " event.step = -1;\n", " }\n", " mouse_event_fn(event);\n", " });\n", "\n", " canvas_div.append(canvas);\n", " canvas_div.append(rubberband);\n", "\n", " this.rubberband = rubberband;\n", " this.rubberband_canvas = rubberband[0];\n", " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", " this.rubberband_context.strokeStyle = \"#000000\";\n", "\n", " this._resize_canvas = function(width, height) {\n", " // Keep the size of the canvas, canvas container, and rubber band\n", " // canvas in synch.\n", " canvas_div.css('width', width)\n", " canvas_div.css('height', height)\n", "\n", " canvas.attr('width', width * mpl.ratio);\n", " canvas.attr('height', height * mpl.ratio);\n", " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", "\n", " rubberband.attr('width', width);\n", " rubberband.attr('height', height);\n", " }\n", "\n", " // Set the figure to an initial 600x600px, this will subsequently be updated\n", " // upon first draw.\n", " this._resize_canvas(600, 600);\n", "\n", " // Disable right mouse context menu.\n", " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", " return false;\n", " });\n", "\n", " function set_focus () {\n", " canvas.focus();\n", " canvas_div.focus();\n", " }\n", "\n", " window.setTimeout(set_focus, 100);\n", "}\n", "\n", "mpl.figure.prototype._init_toolbar = function() {\n", " var fig = this;\n", "\n", " var nav_element = $('')\n", " nav_element.attr('style', 'width: 100%');\n", " this.root.append(nav_element);\n", "\n", " // Define a callback function for later on.\n", " function toolbar_event(event) {\n", " return fig.toolbar_button_onclick(event['data']);\n", " }\n", " function toolbar_mouse_event(event) {\n", " return fig.toolbar_button_onmouseover(event['data']);\n", " }\n", "\n", " for(var toolbar_ind in mpl.toolbar_items) {\n", " var name = mpl.toolbar_items[toolbar_ind][0];\n", " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", " var image = mpl.toolbar_items[toolbar_ind][2];\n", " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", "\n", " if (!name) {\n", " // put a spacer in here.\n", " continue;\n", " }\n", " var button = $('');\n", " button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", " 'ui-button-icon-only');\n", " button.attr('role', 'button');\n", " button.attr('aria-disabled', 'false');\n", " button.click(method_name, toolbar_event);\n", " button.mouseover(tooltip, toolbar_mouse_event);\n", "\n", " var icon_img = $('');\n", " icon_img.addClass('ui-button-icon-primary ui-icon');\n", " icon_img.addClass(image);\n", " icon_img.addClass('ui-corner-all');\n", "\n", " var tooltip_span = $('');\n", " tooltip_span.addClass('ui-button-text');\n", " tooltip_span.html(tooltip);\n", "\n", " button.append(icon_img);\n", " button.append(tooltip_span);\n", "\n", " nav_element.append(button);\n", " }\n", "\n", " var fmt_picker_span = $('');\n", "\n", " var fmt_picker = $('');\n", " fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", " fmt_picker_span.append(fmt_picker);\n", " nav_element.append(fmt_picker_span);\n", " this.format_dropdown = fmt_picker[0];\n", "\n", " for (var ind in mpl.extensions) {\n", " var fmt = mpl.extensions[ind];\n", " var option = $(\n", " '', {selected: fmt === mpl.default_extension}).html(fmt);\n", " fmt_picker.append(option)\n", " }\n", "\n", " // Add hover states to the ui-buttons\n", " $( \".ui-button\" ).hover(\n", " function() { $(this).addClass(\"ui-state-hover\");},\n", " function() { $(this).removeClass(\"ui-state-hover\");}\n", " );\n", "\n", " var status_bar = $('