{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c4a93681",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "import lumicks.pylake as lk\n",
    "\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "642b0d22",
   "metadata": {},
   "source": [
    "# Notebook Widgets\n",
    "\n",
    "[Download this page as a Jupyter notebook](https://lumicks-pylake.readthedocs.io/en/v0.13.3/_downloads/8b3909c096d00c9cddcf8890e275db10/nbwidgets.ipynb)\n",
    "\n",
    "When analyzing notebooks, it can be helpful to make use of interactive widgets. For this, we provide some widgets to help you analyze your data. To enable such widgets, start the notebook with:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "001180bf",
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib widget"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "22ab0a99",
   "metadata": {},
   "source": [
    "## Channel slicing\n",
    "\n",
    "Let’s say we want to do some analyses on slices of channel data. It would be nice to just quickly visually select some regions using a widget. Let’s load the file and run the widget:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3344fe9b",
   "metadata": {},
   "outputs": [],
   "source": [
    "file = lk.File(\"file.h5\")\n",
    "channel = file[\"Force LF\"][\"Force 1x\"]\n",
    "selector = channel.range_selector()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d124298",
   "metadata": {},
   "source": [
    "You can use the left mouse button to select time ranges (by clicking the left and then the right boundary of the region you wish to select). The right mouse button can be used to remove previous selections. We can access the selected timestamps of the ranges we selected by invoking [`selector.ranges`](https://lumicks-pylake.readthedocs.io/en/v0.13.3/tutorial/../_api/lumicks.pylake.FdRangeSelector.html#lumicks.pylake.FdRangeSelector.ranges):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0e6514a7",
   "metadata": {},
   "outputs": [],
   "source": [
    "selector.ranges"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2a6c3a1f",
   "metadata": {},
   "source": [
    "And the actual slices from `selector.slices`. If we want to plot all of our selections in separate plots for instance, we can do the following:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5546e5b1",
   "metadata": {},
   "outputs": [],
   "source": [
    "for data_slice in selector.slices:\n",
    "    plt.figure()\n",
    "    data_slice.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8937927f",
   "metadata": {},
   "source": [
    "## F,d selection\n",
    "\n",
    "### Range selection by time\n",
    "\n",
    "Assume we have an F,d curve we want to analyze. We know that this file contains one F,d curve which should be split up into two segments that we should be analyzing separately:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e5c265f5",
   "metadata": {},
   "outputs": [],
   "source": [
    "fdcurves = file.fdcurves\n",
    "selector = lk.FdRangeSelector(fdcurves)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "225051f9",
   "metadata": {},
   "source": [
    "This opens up a little widget, where you can use the left mouse button to select time ranges and the right mouse button to remove previous selections.\n",
    "\n",
    "Once we’ve selected some time ranges, we can output the timestamps:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "20801fa5",
   "metadata": {},
   "outputs": [],
   "source": [
    "selector.ranges"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "80f841fe",
   "metadata": {},
   "source": [
    "These timestamps can directly be used to extract the relevant data:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cc5917a7",
   "metadata": {},
   "outputs": [],
   "source": [
    "for t_start, t_stop in selector.ranges[\"Fd pull #6\"]:\n",
    "    plt.figure()\n",
    "    plt.plot(fdcurves[\"Fd pull #6\"].f[t_start:t_stop].data)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "685df069",
   "metadata": {},
   "source": [
    "This produces a separate plot for each selection. There’s also a more direct way to get these plots, namely through [`FdRangeSelector.fdcurves`](https://lumicks-pylake.readthedocs.io/en/v0.13.3/tutorial/../_api/lumicks.pylake.FdRangeSelector.html#lumicks.pylake.FdRangeSelector.fdcurves). This gives you an [`FdCurve`](https://lumicks-pylake.readthedocs.io/en/v0.13.3/tutorial/../_api/lumicks.pylake.fdcurve.FdCurve.html#lumicks.pylake.fdcurve.FdCurve) for each section you selected:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7b251d07",
   "metadata": {},
   "outputs": [],
   "source": [
    "for fdcurve in selector.fdcurves[\"Fd pull #6\"]:\n",
    "    plt.figure()\n",
    "    fdcurve.plot_scatter()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9a27dcab",
   "metadata": {},
   "source": [
    "### Processing multiple files\n",
    "\n",
    "Now let’s say our experiment is split up over multiple files, each containing a few F,d curves. We would like to load these curves all at once and make our selections. We can do this using automatically using `glob`. With `glob.glob` we grab a list of all `.h5` files in the directory `my_directory`. We then iterate over this list and open each file. Then, for all those files, we add each individual curves to our variable `fdcurves`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d08206db",
   "metadata": {},
   "outputs": [],
   "source": [
    "import glob\n",
    "\n",
    "fdcurves = {}\n",
    "for filename in glob.glob('my_directory/*.h5'):\n",
    "    file = lk.File(filename)\n",
    "    for key, curve in file.fdcurves.items():\n",
    "        fdcurves[key] = curve"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f7edae3e",
   "metadata": {},
   "source": [
    "Using this dictionary, we can open our widget and see all the data at once:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9166296f",
   "metadata": {},
   "outputs": [],
   "source": [
    "selector = lk.FdRangeSelector(fdcurves)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e8dc39d2",
   "metadata": {},
   "source": [
    "Plotting the curves can be done similarly as before. Here `.values()` indicates that we want the values from the dictionary of curve sets, and not the keys (which in our case are the curve names):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c030098d",
   "metadata": {},
   "outputs": [],
   "source": [
    "for curve_set in selector.fdcurves.values():\n",
    "    for fdcurve in curve_set:\n",
    "        plt.figure()\n",
    "        fdcurve.plot_scatter()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7d3662bb",
   "metadata": {},
   "source": [
    "### Range selection by distance\n",
    "\n",
    "It is also possible to select a portion of an F,d curve based on distance:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "663f11bb",
   "metadata": {},
   "outputs": [],
   "source": [
    "selector = lk.FdDistanceRangeSelector(fdcurves)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "acad759e",
   "metadata": {},
   "source": [
    "Again, we can retrieve the selected data just as with [`FdRangeSelector`](https://lumicks-pylake.readthedocs.io/en/v0.13.3/tutorial/../_api/lumicks.pylake.FdRangeSelector.html#lumicks.pylake.FdRangeSelector):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cede31ca",
   "metadata": {},
   "outputs": [],
   "source": [
    "original = fdcurves[\"Fd pull #6\"]\n",
    "sliced = selector.fdcurves[\"Fd pull #6\"][0]\n",
    "\n",
    "plt.figure()\n",
    "\n",
    "plt.subplot(2, 1, 1)\n",
    "original.plot_scatter(label=\"original\")\n",
    "sliced.plot_scatter(label=\"sliced\")\n",
    "plt.legend()\n",
    "\n",
    "plt.subplot(2, 1, 2)\n",
    "original.f.plot()\n",
    "sliced.f.plot(start=original.start)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "507e3196",
   "metadata": {},
   "source": [
    "The returned F,d curves correspond to the longest contiguous (in time) stretch of data that falls within the distance thresholds. However, noise in the distance measurement can lead to short gaps of the time trace falling slightly outside of the thresholds, as illustrated below:\n",
    "\n",
    "To avoid premature truncation caused by this noise, there is an additional `max_gap` keyword argument to [`FdDistanceRangeSelector`](https://lumicks-pylake.readthedocs.io/en/v0.13.3/tutorial/../_api/lumicks.pylake.FdDistanceRangeSelector.html#lumicks.pylake.FdDistanceRangeSelector) that can be used to adjust the acceptable length of noise gaps. The default values is zero, such that all data points are guaranteed to fall within the selected distance range. The effect of this argument is shown below for an F,d curve sliced with the same distance thresholds:\n",
    "\n",
    "### Range selection of single curve\n",
    "\n",
    "The selector widgets can also be easily accessed from single F,d curve instances:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a8e89dea",
   "metadata": {},
   "outputs": [],
   "source": [
    "fdcurve = fdcurves[\"Fd pull #6\"]\n",
    "t_selector = fdcurve.range_selector()\n",
    "d_selector = fdcurve.distance_range_selector(max_gap=3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fb57d0fb",
   "metadata": {},
   "source": [
    "## Cropping and Rotating Image Stacks\n",
    "\n",
    "You can interactively define the location of a tether for a [`ImageStack`](https://lumicks-pylake.readthedocs.io/en/v0.13.3/tutorial/../_api/lumicks.pylake.ImageStack.html#lumicks.pylake.ImageStack) by using:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c1088770",
   "metadata": {},
   "outputs": [],
   "source": [
    "stack = lk.ImageStack(\"cas9_wf.tiff\")\n",
    "editor = stack.crop_and_rotate()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2b8291dd",
   "metadata": {},
   "source": [
    "Simply left-click on the start of the tether\n",
    "\n",
    "and then on the end of the tether\n",
    "\n",
    "After a tether is defined, the view will update showing the location of the tether and the image rotated such that the tether is horizontal.\n",
    "\n",
    "To crop an image, right-click and drag a rectangle around the region of interest. Once the rectangle is defined, you can edit the shape by right-clicking and dragging the various handles.\n",
    "\n",
    "You can also use the mouse wheel to scroll through the individual frames (if using Jupyter Lab, hold `Shift` while scrolling).\n",
    "\n",
    "Note that [`ImageStack.crop_and_rotate()`](https://lumicks-pylake.readthedocs.io/en/v0.13.3/tutorial/../_api/lumicks.pylake.ImageStack.html#lumicks.pylake.ImageStack.crop_and_rotate) accepts all of the arguments that can be used for [`ImageStack.plot()`](https://lumicks-pylake.readthedocs.io/en/v0.13.3/tutorial/../_api/lumicks.pylake.ImageStack.html#lumicks.pylake.ImageStack.plot).\n",
    "\n",
    "To obtain a copy of the edited [`ImageStack`](https://lumicks-pylake.readthedocs.io/en/v0.13.3/tutorial/../_api/lumicks.pylake.ImageStack.html#lumicks.pylake.ImageStack) object, use:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "955a7bd5",
   "metadata": {},
   "outputs": [],
   "source": [
    "new_stack = editor.image\n",
    "new_stack.plot()\n",
    "new_stack.plot_tether()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "227d2d79",
   "metadata": {},
   "source": [
    "## Kymotracking\n",
    "\n",
    "<div class=\"alert alert-block alert-info\"><b>Note: </b>For details of the tracking algorithms and downstream analyses see the [Kymotracking](https://lumicks-pylake.readthedocs.io/en/v0.13.3/tutorial/kymotracking.html) tutorial.</div>\n",
    "\n",
    "For tracking binding events on a kymograph, using the [`track_greedy()`](https://lumicks-pylake.readthedocs.io/en/v0.13.3/tutorial/../_api/lumicks.pylake.track_greedy.html#lumicks.pylake.track_greedy) algorithm purely by function calls can be challenging if not all parts of the kymograph look the same or when the signal to noise ratio is somewhat low. To help with this, we included a kymotracking widget that can help you track subsections of the kymograph and iteratively tweak the algorithm parameters as you do so. You can open this widget by invoking the following command:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "775cf7e8",
   "metadata": {},
   "outputs": [],
   "source": [
    "kymowidget = lk.KymoWidgetGreedy(kymo, \"green\", axis_aspect_ratio=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6717e5d6",
   "metadata": {},
   "source": [
    "Here we see the optional `axis_aspect_ratio` argument that allows us to control the aspect ratio of the plot and how much data is visible at a given time. You can easily pan horizontally by clicking and dragging left or right.\n",
    "\n",
    "You can optionally also pass algorithm parameters when opening the widget:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5644eac8",
   "metadata": {},
   "outputs": [],
   "source": [
    "lk.KymoWidgetGreedy(kymo, \"green\", axis_aspect_ratio=2, min_length=4, pixel_threshold=3, window=6, sigma=0.14)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7acc5f62",
   "metadata": {},
   "source": [
    "You can also change the range of each of the algorithm parameter sliders. To do this, simply pass a dictionary where the key indicates the algorithm parameter and the value contains its desired range in the form `(minimum bound, maximum bound)`. For example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "17ead7da",
   "metadata": {},
   "outputs": [],
   "source": [
    "lk.KymoWidgetGreedy(kymo, \"green\", axis_aspect_ratio=2, slider_ranges={\"window\": (0, 8)})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f5e0cf28",
   "metadata": {},
   "source": [
    "Detected tracks are accessible through the `.tracks` property:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c6258685",
   "metadata": {},
   "outputs": [],
   "source": [
    "tracks = kymowidget.tracks\n",
    "print(tracks)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "617256b8",
   "metadata": {},
   "source": [
    "For more information on its use, please see the example [Analyzing Cas9 binding to DNA](https://lumicks-pylake.readthedocs.io/en/v0.13.3/tutorial/../examples/cas9_kymotracking/cas9_kymotracking.html#id1)."
   ]
  }
 ],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 5
}