Skip to content

Commit 0e3def8

Browse files
committed
Add complex applications authoring section
Signed-off-by: Itay Dafna <[email protected]>
1 parent 9827970 commit 0e3def8

File tree

5 files changed

+356
-0
lines changed

5 files changed

+356
-0
lines changed
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"id": "f8da2924-07bb-4e1e-9372-6ff2685a5312",
6+
"metadata": {},
7+
"source": [
8+
"# Building complex widget libraries"
9+
]
10+
},
11+
{
12+
"cell_type": "markdown",
13+
"id": "e31c1960-d4db-4681-8cbf-5d88b82c5e50",
14+
"metadata": {
15+
"tags": []
16+
},
17+
"source": [
18+
"## The problem\n",
19+
"Often when composing widgets into an application, it becomes cumbersome to find a good way of managing state and keeping track of variables as the application grows. This notebooks aims to provide gentle guidance for managing complex application libraries. Please bear in mind these are merely **suggestions** and are by no means the only, or even best way of going about this."
20+
]
21+
},
22+
{
23+
"cell_type": "markdown",
24+
"id": "82d1eef5-52fe-4b37-8c80-cb812cbd4324",
25+
"metadata": {},
26+
"source": [
27+
"### The current approach"
28+
]
29+
},
30+
{
31+
"cell_type": "code",
32+
"execution_count": null,
33+
"id": "5a7f24bc-4df9-481a-a5c3-380485d5ff35",
34+
"metadata": {},
35+
"outputs": [],
36+
"source": [
37+
"# Generating data\n",
38+
"import numpy as np\n",
39+
"import pandas as pd\n",
40+
"\n",
41+
"np.random.seed(0)\n",
42+
"p_t, n = 100, 260\n",
43+
"stock_df = pd.DataFrame({f'Stock {i}': p_t + np.round(np.random.standard_normal(n).cumsum(), 2) for i in range(10)})"
44+
]
45+
},
46+
{
47+
"cell_type": "code",
48+
"execution_count": null,
49+
"id": "488c6461-0e3c-4234-ad4c-f03ef6115996",
50+
"metadata": {},
51+
"outputs": [],
52+
"source": [
53+
"# Imports\n",
54+
"from bqplot import LinearScale, Axis, Figure, Lines, CATEGORY10\n",
55+
"from ipywidgets import HBox, VBox, Layout, HTML\n",
56+
"from ipydatagrid import DataGrid\n",
57+
" \n",
58+
"# Setting up the data grid\n",
59+
"stock_grid = DataGrid(stock_df, selection_mode='column')\n",
60+
"\n",
61+
"# Creating the bqplot chart objects\n",
62+
"sc_x = LinearScale()\n",
63+
"sc_y = LinearScale()\n",
64+
"line = Lines(x=[], y=[], labels=['Fake stock price'], display_legend=True,\n",
65+
" scales={'x': sc_x, 'y': sc_y})\n",
66+
"ax_x = Axis(scale=sc_x, label='Index')\n",
67+
"ax_y = Axis(scale=sc_y, orientation='vertical', label='y-value')\n",
68+
"fig = Figure(marks=[line], axes=[ax_x, ax_y], title='Line Chart', layout=Layout(flex='1 1 auto', width='100%'))\n",
69+
"\n",
70+
"# Creating the application title\n",
71+
"app_title = HTML(value=\"<h1 style='color: salmon'>My complex application</h1>\")"
72+
]
73+
},
74+
{
75+
"cell_type": "code",
76+
"execution_count": null,
77+
"id": "4d84c7b3-451c-4c88-b59e-4d0ae3d578e6",
78+
"metadata": {},
79+
"outputs": [],
80+
"source": [
81+
"# Define callbacks\n",
82+
"def plot_stock(*args):\n",
83+
" line.y = stock_grid.selected_cell_values\n",
84+
" line.x = range(len(line.y))\n",
85+
" column_index = stock_grid.selections[0]['c1']\n",
86+
" line.labels = [stock_df.columns[column_index]]\n",
87+
" line.colors = [CATEGORY10[np.random.randint(0, len(CATEGORY10)) % len(CATEGORY10)]]\n",
88+
" \n",
89+
"# Event listener for cell click\n",
90+
"stock_grid.observe(plot_stock, names='selections')"
91+
]
92+
},
93+
{
94+
"cell_type": "code",
95+
"execution_count": null,
96+
"id": "79e135e3-b37c-457b-a268-3695a3def263",
97+
"metadata": {},
98+
"outputs": [],
99+
"source": [
100+
"VBox([\n",
101+
" app_title,\n",
102+
" HBox(\n",
103+
" [stock_grid, fig]\n",
104+
" )\n",
105+
"])"
106+
]
107+
},
108+
{
109+
"cell_type": "markdown",
110+
"id": "5c635ec7-c85e-425b-ab5e-f3e4ad937a37",
111+
"metadata": {},
112+
"source": [
113+
"### A more structured approach"
114+
]
115+
},
116+
{
117+
"cell_type": "code",
118+
"execution_count": null,
119+
"id": "47f6bfa5-b044-4da2-bac8-d3066ac1532d",
120+
"metadata": {},
121+
"outputs": [],
122+
"source": [
123+
"class Chart:\n",
124+
" def __init__(self, figure_title='Line Chart'):\n",
125+
" self._sc_x = LinearScale()\n",
126+
" self._sc_y = LinearScale()\n",
127+
" self._line = Lines(x=[], y=[], labels=['Fake stock price'], display_legend=True,\n",
128+
" scales={'x': self._sc_x, 'y': self._sc_y})\n",
129+
" self._ax_x = Axis(scale=self._sc_x, label='Index')\n",
130+
" self._ax_y = Axis(scale=self._sc_y, orientation='vertical', label='y-value')\n",
131+
" self._fig = Figure(marks=[self._line], axes=[self._ax_x, self._ax_y], title=figure_title, layout=Layout(flex='1 1 auto', width='100%'))\n",
132+
" \n",
133+
" def get_figure(self):\n",
134+
" return self._fig\n",
135+
" \n",
136+
" def get_line(self):\n",
137+
" return self._line\n",
138+
" \n",
139+
" def set_line(self, x, y, labels, colors):\n",
140+
" self._line.x = x\n",
141+
" self._line.y = y\n",
142+
" self._line.labels = labels\n",
143+
" self._line.colors = colors"
144+
]
145+
},
146+
{
147+
"cell_type": "code",
148+
"execution_count": null,
149+
"id": "edaf116c-d873-4a24-ba8c-9536de90e4cc",
150+
"metadata": {},
151+
"outputs": [],
152+
"source": [
153+
"from IPython.display import display\n",
154+
"\n",
155+
"class MyApplication:\n",
156+
" def __init__(self, data, application_title='My complex application'):\n",
157+
" self.dataframe = data\n",
158+
" self.datagrid = self.process_data(data)\n",
159+
" self.chart = Chart()\n",
160+
" self.app_title = HTML(value=f\"<h1 style='color: salmon'>{application_title}</h1>\")\n",
161+
" self.run_application()\n",
162+
" \n",
163+
" def process_data(self, dataframe):\n",
164+
" return DataGrid(dataframe, selection_mode='column')\n",
165+
" \n",
166+
" def generate_layout(self):\n",
167+
" return VBox([self.app_title, HBox([self.datagrid, self.chart.get_figure()])])\n",
168+
" \n",
169+
" def setup_event_handlers(self):\n",
170+
" self.datagrid.observe(self.plot_stock, names='selections')\n",
171+
" \n",
172+
" def run_application(self):\n",
173+
" self.setup_event_handlers()\n",
174+
" display(self.generate_layout())\n",
175+
" \n",
176+
" # Callbacks section\n",
177+
" def plot_stock(self, *args):\n",
178+
" column_index = self.datagrid.selections[0]['c1']\n",
179+
" line = self.chart.get_line()\n",
180+
" selected_values = self.datagrid.selected_cell_values\n",
181+
" self.chart.set_line(\n",
182+
" range(len(selected_values)), \n",
183+
" selected_values, \n",
184+
" [self.dataframe.columns[column_index]], \n",
185+
" [CATEGORY10[np.random.randint(0, len(CATEGORY10)) % len(CATEGORY10)]]\n",
186+
" )"
187+
]
188+
},
189+
{
190+
"cell_type": "code",
191+
"execution_count": null,
192+
"id": "05fdd2fa-ecac-4b1e-add4-362a8814f491",
193+
"metadata": {},
194+
"outputs": [],
195+
"source": [
196+
"app = MyApplication(data=stock_df, application_title=\"An alternative approach\")"
197+
]
198+
}
199+
],
200+
"metadata": {
201+
"kernelspec": {
202+
"display_name": "Python 3 (ipykernel)",
203+
"language": "python",
204+
"name": "python3"
205+
},
206+
"language_info": {
207+
"codemirror_mode": {
208+
"name": "ipython",
209+
"version": 3
210+
},
211+
"file_extension": ".py",
212+
"mimetype": "text/x-python",
213+
"name": "python",
214+
"nbconvert_exporter": "python",
215+
"pygments_lexer": "ipython3",
216+
"version": "3.9.13"
217+
}
218+
},
219+
"nbformat": 4,
220+
"nbformat_minor": 5
221+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"id": "413439cf-360f-435b-acfe-4b1cc9042773",
6+
"metadata": {},
7+
"source": [
8+
"# Refactoring the class-based implementation"
9+
]
10+
},
11+
{
12+
"cell_type": "code",
13+
"execution_count": null,
14+
"id": "36cd43c4-9df2-40cf-a4f0-66e660af7035",
15+
"metadata": {},
16+
"outputs": [],
17+
"source": [
18+
"# Generating data\n",
19+
"import numpy as np\n",
20+
"import pandas as pd\n",
21+
"\n",
22+
"np.random.seed(0)\n",
23+
"p_t, n = 100, 260\n",
24+
"stock_df = pd.DataFrame({f'Stock {i}': p_t + np.round(np.random.standard_normal(n).cumsum(), 2) for i in range(10)})"
25+
]
26+
},
27+
{
28+
"cell_type": "code",
29+
"execution_count": null,
30+
"id": "bde0b4dd-96bf-441c-a7b1-3e4ab37cd7c5",
31+
"metadata": {},
32+
"outputs": [],
33+
"source": [
34+
"from my_application import MyApplication"
35+
]
36+
},
37+
{
38+
"cell_type": "code",
39+
"execution_count": null,
40+
"id": "e04c0045-f815-4c7b-a0a4-dee9c004275a",
41+
"metadata": {},
42+
"outputs": [],
43+
"source": [
44+
"app = MyApplication(stock_df)"
45+
]
46+
}
47+
],
48+
"metadata": {
49+
"kernelspec": {
50+
"display_name": "Python 3 (ipykernel)",
51+
"language": "python",
52+
"name": "python3"
53+
},
54+
"language_info": {
55+
"codemirror_mode": {
56+
"name": "ipython",
57+
"version": 3
58+
},
59+
"file_extension": ".py",
60+
"mimetype": "text/x-python",
61+
"name": "python",
62+
"nbconvert_exporter": "python",
63+
"pygments_lexer": "ipython3",
64+
"version": "3.9.13"
65+
}
66+
},
67+
"nbformat": 4,
68+
"nbformat_minor": 5
69+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .application import MyApplication
2+
3+
__all__ = ["MyApplication"]
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from IPython.display import display
2+
from ipywidgets import HBox, VBox, HTML
3+
from ipydatagrid import DataGrid
4+
from bqplot import CATEGORY10
5+
from .chart import Chart
6+
import numpy as np
7+
8+
class MyApplication:
9+
def __init__(self, data, application_title='My complex application'):
10+
self.dataframe = data
11+
self.datagrid = self.process_data(data)
12+
self.chart = Chart()
13+
self.app_title = HTML(value=f"<h1 style='color: salmon'>{application_title}</h1>")
14+
self.run_application()
15+
16+
def process_data(self, dataframe):
17+
return DataGrid(dataframe, selection_mode='column')
18+
19+
def generate_layout(self):
20+
return VBox([self.app_title, HBox([self.datagrid, self.chart.get_figure()])])
21+
22+
def setup_event_handlers(self):
23+
self.datagrid.observe(self.plot_stock, names='selections')
24+
25+
def run_application(self):
26+
self.setup_event_handlers()
27+
display(self.generate_layout())
28+
29+
# Callbacks section
30+
def plot_stock(self, *args):
31+
column_index = self.datagrid.selections[0]['c1']
32+
line = self.chart.get_line()
33+
selected_values = self.datagrid.selected_cell_values
34+
self.chart.set_line(
35+
range(len(selected_values)),
36+
selected_values,
37+
[self.dataframe.columns[column_index]],
38+
[CATEGORY10[np.random.randint(0, len(CATEGORY10)) % len(CATEGORY10)]]
39+
)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from bqplot import LinearScale, Axis, Figure, Lines, CATEGORY10
2+
from ipywidgets import Layout
3+
4+
class Chart:
5+
def __init__(self, figure_title='Line Chart'):
6+
self._sc_x = LinearScale()
7+
self._sc_y = LinearScale()
8+
self._line = Lines(x=[], y=[], labels=['Fake stock price'], display_legend=True,
9+
scales={'x': self._sc_x, 'y': self._sc_y})
10+
self._ax_x = Axis(scale=self._sc_x, label='Index')
11+
self._ax_y = Axis(scale=self._sc_y, orientation='vertical', label='y-value')
12+
self._fig = Figure(marks=[self._line], axes=[self._ax_x, self._ax_y], title=figure_title, layout=Layout(flex='1 1 auto', width='100%'))
13+
14+
def get_figure(self):
15+
return self._fig
16+
17+
def get_line(self):
18+
return self._line
19+
20+
def set_line(self, x, y, labels, colors):
21+
self._line.x = x
22+
self._line.y = y
23+
self._line.labels = labels
24+
self._line.colors = colors

0 commit comments

Comments
 (0)