{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Example demonstrating a variety of scatter plot features.\n",
    "\"\"\"\n",
    "from pyqtgraph.jupyter import GraphicsLayoutWidget\n",
    "import ipywidgets\n",
    "from IPython.display import display\n",
    "\n",
    "from pyqtgraph.Qt import QtGui\n",
    "import pyqtgraph as pg\n",
    "import numpy as np\n",
    "from collections import namedtuple\n",
    "\n",
    "pg.mkQApp()\n",
    "view = GraphicsLayoutWidget(css_width=\"800px\", css_height=\"800px\")\n",
    "\n",
    "## create four areas to add plots\n",
    "w1 = view.addPlot()\n",
    "w2 = view.addViewBox()\n",
    "w2.setAspectLocked(True)\n",
    "view.nextRow()\n",
    "w3 = view.addPlot()\n",
    "w4 = view.addPlot()\n",
    "print(\"Generating data, this takes a few seconds...\")\n",
    "\n",
    "## Make all plots clickable\n",
    "clickedPen = pg.mkPen('b', width=2)\n",
    "lastClicked = []\n",
    "def clicked(plot, points):\n",
    "    global lastClicked\n",
    "    for p in lastClicked:\n",
    "        p.resetPen()\n",
    "    with out:\n",
    "        print(\"clicked points\", points)\n",
    "    for p in points:\n",
    "        p.setPen(clickedPen)\n",
    "    lastClicked = points\n",
    "    view.request_draw()\n",
    "\n",
    "\n",
    "## There are a few different ways we can draw scatter plots; each is optimized for different types of data:\n",
    "\n",
    "## 1) All spots identical and transform-invariant (top-left plot).\n",
    "## In this case we can get a huge performance boost by pre-rendering the spot\n",
    "## image and just drawing that image repeatedly.\n",
    "\n",
    "n = 300\n",
    "s1 = pg.ScatterPlotItem(size=10, pen=pg.mkPen(None), brush=pg.mkBrush(255, 255, 255, 120))\n",
    "pos = np.random.normal(size=(2,n), scale=1e-5)\n",
    "spots = [{'pos': pos[:,i], 'data': 1} for i in range(n)] + [{'pos': [0,0], 'data': 1}]\n",
    "s1.addPoints(spots)\n",
    "w1.addItem(s1)\n",
    "s1.sigClicked.connect(clicked)\n",
    "\n",
    "\n",
    "## 2) Spots are transform-invariant, but not identical (top-right plot).\n",
    "## In this case, drawing is almsot as fast as 1), but there is more startup\n",
    "## overhead and memory usage since each spot generates its own pre-rendered\n",
    "## image.\n",
    "\n",
    "TextSymbol = namedtuple(\"TextSymbol\", \"label symbol scale\")\n",
    "\n",
    "def createLabel(label, angle):\n",
    "    symbol = QtGui.QPainterPath()\n",
    "    #symbol.addText(0, 0, QFont(\"San Serif\", 10), label)\n",
    "    f = QtGui.QFont()\n",
    "    f.setPointSize(10)\n",
    "    symbol.addText(0, 0, f, label)\n",
    "    br = symbol.boundingRect()\n",
    "    scale = min(1. / br.width(), 1. / br.height())\n",
    "    tr = QtGui.QTransform()\n",
    "    tr.scale(scale, scale)\n",
    "    tr.rotate(angle)\n",
    "    tr.translate(-br.x() - br.width()/2., -br.y() - br.height()/2.)\n",
    "    return TextSymbol(label, tr.map(symbol), 0.1 / scale)\n",
    "\n",
    "random_str = lambda : (''.join([chr(np.random.randint(ord('A'),ord('z'))) for i in range(np.random.randint(1,5))]), np.random.randint(0, 360))\n",
    "\n",
    "s2 = pg.ScatterPlotItem(size=10, pen=pg.mkPen('w'), pxMode=True)\n",
    "pos = np.random.normal(size=(2,n), scale=1e-5)\n",
    "spots = [{'pos': pos[:,i], 'data': 1, 'brush':pg.intColor(i, n), 'symbol': i%10, 'size': 5+i/10.} for i in range(n)]\n",
    "s2.addPoints(spots)\n",
    "spots = [{'pos': pos[:,i], 'data': 1, 'brush':pg.intColor(i, n), 'symbol': label[1], 'size': label[2]*(5+i/10.)} for (i, label) in [(i, createLabel(*random_str())) for i in range(n)]]\n",
    "s2.addPoints(spots)\n",
    "w2.addItem(s2)\n",
    "s2.sigClicked.connect(clicked)\n",
    "\n",
    "\n",
    "## 3) Spots are not transform-invariant, not identical (bottom-left).\n",
    "## This is the slowest case, since all spots must be completely re-drawn\n",
    "## every time because their apparent transformation may have changed.\n",
    "\n",
    "s3 = pg.ScatterPlotItem(\n",
    "    pxMode=False,  # Set pxMode=False to allow spots to transform with the view\n",
    "    hoverable=True,\n",
    "    hoverPen=pg.mkPen('g'),\n",
    "    hoverSize=1e-6\n",
    ")\n",
    "spots3 = []\n",
    "for i in range(10):\n",
    "    for j in range(10):\n",
    "        spots3.append({'pos': (1e-6*i, 1e-6*j), 'size': 1e-6, 'pen': {'color': 'w', 'width': 2}, 'brush':pg.intColor(i*10+j, 100)})\n",
    "s3.addPoints(spots3)\n",
    "w3.addItem(s3)\n",
    "s3.sigClicked.connect(clicked)\n",
    "\n",
    "## Test performance of large scatterplots\n",
    "\n",
    "s4 = pg.ScatterPlotItem(\n",
    "    size=10,\n",
    "    pen=pg.mkPen(None),\n",
    "    brush=pg.mkBrush(255, 255, 255, 20),\n",
    "    hoverable=True,\n",
    "    hoverSymbol='s',\n",
    "    hoverSize=15,\n",
    "    hoverPen=pg.mkPen('r', width=2),\n",
    "    hoverBrush=pg.mkBrush('g'),\n",
    ")\n",
    "n = 10000\n",
    "pos = np.random.normal(size=(2, n), scale=1e-9)\n",
    "s4.addPoints(\n",
    "    x=pos[0],\n",
    "    y=pos[1],\n",
    "    # size=(np.random.random(n) * 20.).astype(int),\n",
    "    # brush=[pg.mkBrush(x) for x in np.random.randint(0, 256, (n, 3))],\n",
    "    data=np.arange(n)\n",
    ")\n",
    "w4.addItem(s4)\n",
    "s4.sigClicked.connect(clicked)\n",
    "\n",
    "\n",
    "out = ipywidgets.Output(layout={'border': '1px solid black'})\n",
    "display(view)\n",
    "display(out)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
