|
| 1 | +<!-- markdownlint-disable MD033 --> |
| 2 | + |
| 3 | +# A Demo Form |
| 4 | + |
| 5 | +This is a detailed example demonstrating the usage of ScriptedForms. |
| 6 | + |
| 7 | +To have this document help you the most ideally you should have it open both as |
| 8 | +a ScriptedForm and as a plain text document. If you open the document itself |
| 9 | +in a plain text editor (such as [Visual Studio Code](https://code.visualstudio.com/)) |
| 10 | +then within the same directory as this document run `scriptedforms detailed.md` |
| 11 | +within a terminal you will be able to see the resulting form, and the syntax |
| 12 | +that produces it. You will also be able to edit the file and see the changes |
| 13 | +live within the ScriptedForm. |
| 14 | + |
| 15 | +## Description |
| 16 | + |
| 17 | +This file format is based upon markdown. There are however a few |
| 18 | +extra custom html elements. The custom html elements come in two |
| 19 | +types, either section elements `<section-something>` or |
| 20 | +variable elements `<variable-something>`. |
| 21 | + |
| 22 | +## Custom elements overview |
| 23 | + |
| 24 | +### Sections overview |
| 25 | + |
| 26 | +#### Running Python code |
| 27 | + |
| 28 | +Whenever python fenced code blocks are written within a section that code is |
| 29 | +no longer displayed within the document, instead it is sent to the Python |
| 30 | +kernel when certain conditions are fulfilled and inplace of where the code was |
| 31 | +written the output of that code is displayed. Code output is displayed as it |
| 32 | +would display within a Jupyter Notebook. |
| 33 | + |
| 34 | +Python fenced code is written as follows: |
| 35 | + |
| 36 | +```python |
| 37 | +print('Hello World!') |
| 38 | +``` |
| 39 | + |
| 40 | +#### Section types |
| 41 | + |
| 42 | +There are four kinds of sections |
| 43 | + |
| 44 | +* `<section-start>`, |
| 45 | +* `<section-live>`, |
| 46 | +* `<section-button>`, |
| 47 | +* and `<section-output>`. |
| 48 | + |
| 49 | +Code which is written inside of these defined sections is run |
| 50 | +as python code according to specific rules. |
| 51 | + |
| 52 | +### Variable overview |
| 53 | + |
| 54 | +Variable elements are attached to a specific python variable which update on |
| 55 | +user input. All variable elements take just one item placed between the |
| 56 | +open and close braces. That item is |
| 57 | +the Python variable definition. It doesn't strictly have to be a Python variable |
| 58 | +it merely has to be valid Python code to exist on the left hand side of an assignment |
| 59 | +equal sign. |
| 60 | + |
| 61 | +There are six kinds of variable inputs: |
| 62 | + |
| 63 | +* `<variable-number>`, |
| 64 | +* `<variable-slider>`, |
| 65 | +* `<variable-table>`, |
| 66 | +* `<variable-tick>`, |
| 67 | +* `<variable-toggle>`, |
| 68 | +* `<variable-string>`, |
| 69 | +* and `<variable-dropdown>`. |
| 70 | + |
| 71 | +`<variable-number>` and `<variable-slider>` represent floats or integers. |
| 72 | +`<variable-string>` represents a Python string. `<variable-tick>` and `<variable-toggle>` |
| 73 | +are both booleans. The `<variable-table>` is a pandas dataframe with all of the values |
| 74 | +in the dataframe being floats or integers. `<variable-dropdown>` is a string with provided options. |
| 75 | + |
| 76 | +## Usage of the scriptedforms elements |
| 77 | + |
| 78 | +### Start sections |
| 79 | + |
| 80 | +Whenever a jupyterlab services session is started |
| 81 | +code within the start sections is run first. |
| 82 | + |
| 83 | +If you reopen or update the form template without restarting the kernel |
| 84 | +this code will not re-run however a button will appear that will allow you to |
| 85 | +manually re-run the code if need be. |
| 86 | + |
| 87 | +An example `<section-start>` is given following: |
| 88 | + |
| 89 | +<section-start> |
| 90 | + |
| 91 | +```python |
| 92 | +import time |
| 93 | +from IPython.display import display, Markdown |
| 94 | + |
| 95 | +plt.rc('font', size=15) |
| 96 | + |
| 97 | +data = np.ones(3) * np.nan |
| 98 | +data[0] = 5 |
| 99 | + |
| 100 | +table = pd.DataFrame( |
| 101 | + columns=['Meas1', 'Meas2', 'Meas3', 'Avg'], |
| 102 | + index=['6MV', '10MV'], |
| 103 | + data=[[1,np.nan,np.nan,np.nan],[4,5,np.nan,np.nan]]) |
| 104 | + |
| 105 | +hello = False |
| 106 | +world = False |
| 107 | +bye = False |
| 108 | + |
| 109 | +machine = None |
| 110 | + |
| 111 | +submit_count = 0 |
| 112 | +output_count = 0 |
| 113 | + |
| 114 | +custom_machine = '' |
| 115 | + |
| 116 | +display(Markdown('This is the output of a start section')) |
| 117 | +``` |
| 118 | + |
| 119 | +</section-start> |
| 120 | + |
| 121 | +As can be seen from this code there are already a few namespaces included by |
| 122 | +default within the Python session. Some of these are for convenience, some are |
| 123 | +required for the proper running of the form. The code that is run at boot of |
| 124 | +a new form kernel can be found within the |
| 125 | +[source code](https://github.com/SimonBiggs/scriptedforms/blob/master/scriptedforms/src/app/form-builder-module/session-start-code.ts). |
| 126 | + |
| 127 | +### Live sections and demo of each of the variable types |
| 128 | + |
| 129 | +The `<section-live>` element is designed to contain both code and variable inputs. Whenever |
| 130 | +the user changes any variable within the live section all code that is also |
| 131 | +contained within that live section is subsequently run. |
| 132 | + |
| 133 | +Each of the usable variables are demoed below making use of `<section-live>`. |
| 134 | + |
| 135 | +#### Number and slider variables |
| 136 | + |
| 137 | +Below is a `<section-live>` containing both `<variable-number>` and |
| 138 | +`<variable-slider>` elements. They are using a numpy array that has previously |
| 139 | +been defined within the `<section-start>`. |
| 140 | + |
| 141 | +<section-live> |
| 142 | +<variable-number>data[0]</variable-number> |
| 143 | +<variable-number>data[1]</variable-number> |
| 144 | + |
| 145 | +<variable-slider>data[0]</variable-slider> |
| 146 | +<variable-slider>data[1]</variable-slider> |
| 147 | + |
| 148 | +```python |
| 149 | +plt.figure(figsize=(5*1.618,5)) |
| 150 | +plt.plot(data, 'o'); |
| 151 | +``` |
| 152 | + |
| 153 | +`<variable-number>` and `<variable-slider>` both have four optional parameters: |
| 154 | + |
| 155 | +* `label`, for changing the visible label of the input |
| 156 | +* `min` and `max`, changing the range of the input |
| 157 | +* `step` for changing the step size of the input |
| 158 | + |
| 159 | +Min and max defaults to `null` for `<variable-number>` and defaults to 0 and 100 |
| 160 | +respectively for `<variable-slider>`. Step defaults to 1 for both elements. |
| 161 | + |
| 162 | +The use of these optional parameters is demoed below: |
| 163 | + |
| 164 | +<variable-number label="A custom label" min="0" max="10" step="0.1">data[2]</variable-number> |
| 165 | +<variable-slider label="A custom label" min="0" max="10" step="0.1">data[2]</variable-slider> |
| 166 | + |
| 167 | +</section-live> |
| 168 | + |
| 169 | +#### Example slider use case |
| 170 | + |
| 171 | +Using the slider and live sections combined with matplotlib plots you can |
| 172 | +produce utilities like the following: |
| 173 | + |
| 174 | +<section-start> |
| 175 | + |
| 176 | +```python |
| 177 | +t = np.linspace(-2*np.pi, 2*np.pi, 500) |
| 178 | +omega = np.ones(2) |
| 179 | +``` |
| 180 | + |
| 181 | +</section-start> |
| 182 | + |
| 183 | +<section-live> |
| 184 | + |
| 185 | +Angular frequencies ($\omega$): |
| 186 | + |
| 187 | +<variable-slider label="$\omega [0]$" min="0" max="6" step="0.1">omega[0]</variable-slider> |
| 188 | +<variable-slider label="$\omega [1]$" min="0" max="6" step="0.1">omega[1]</variable-slider> |
| 189 | + |
| 190 | +```python |
| 191 | +plt.figure(figsize=(5*1.618,5)) |
| 192 | + |
| 193 | +oscillation = np.sin(t[:, np.newaxis] * omega[np.newaxis, :]) |
| 194 | +summation = np.sum(oscillation, axis=1) |
| 195 | + |
| 196 | +plt.plot(t, oscillation) |
| 197 | +plt.plot(t, summation) |
| 198 | + |
| 199 | +plt.xlim([-2*np.pi, 2*np.pi]) |
| 200 | +plt.ylim([-2.8, 2.8]) |
| 201 | +plt.title('Two sin curves and their summation') |
| 202 | +plt.legend([ |
| 203 | + r'$\omega [0] = {0:0.1f}$'.format(omega[0]), |
| 204 | + r'$\omega [1] = {0:0.1f}$'.format(omega[1]), |
| 205 | + 'Summation'], loc='upper right') |
| 206 | +plt.xlabel('time (seconds)') |
| 207 | +plt.ylabel(r'$sin(\omega \times t)$'); |
| 208 | +``` |
| 209 | + |
| 210 | +</section-live> |
| 211 | + |
| 212 | +#### Table variables |
| 213 | + |
| 214 | +Table variables display a full pandas dataframe. The live code can update one |
| 215 | +part of the table as other parts are being edited. |
| 216 | + |
| 217 | +<section-live> |
| 218 | +<variable-table inputTypes="{'Meas1': 'number', 'Meas2': 'number', 'Meas3': 'number', 'Avg': 'readonly'}">table</variable-table> |
| 219 | + |
| 220 | +```python |
| 221 | +table.iloc[:,3] = np.nanmean(table.iloc[:,0:3], axis=1) |
| 222 | +``` |
| 223 | + |
| 224 | +</section-live> |
| 225 | + |
| 226 | +#### The tick and toggle variables |
| 227 | + |
| 228 | +Tick and toggle variables are simply different representations of a True/False |
| 229 | +boolean variable within python. They are provided for use cases such as check |
| 230 | +lists and pass fail tests. These variables can interact with each other in |
| 231 | +interesting ways via the live Python code. |
| 232 | + |
| 233 | +<section-live> |
| 234 | +<variable-tick>hello</variable-tick> |
| 235 | + |
| 236 | +<variable-tick>world</variable-tick> |
| 237 | + |
| 238 | +```python |
| 239 | +if bye: |
| 240 | + hello = False |
| 241 | + world = False |
| 242 | + |
| 243 | +if hello and world: |
| 244 | + display(Markdown('Hello World!')) |
| 245 | +else: |
| 246 | + display(Markdown('')) |
| 247 | +``` |
| 248 | + |
| 249 | +<variable-toggle>bye</variable-toggle> |
| 250 | +</section-live> |
| 251 | + |
| 252 | +#### String variables |
| 253 | + |
| 254 | +String variables fill the entire width of the container they are in. They also |
| 255 | +expand when new lines are provied. An example use case is an optional notes |
| 256 | +field. |
| 257 | + |
| 258 | +The Python code for this notes field takes what is written and renders it as |
| 259 | +markdown. Try writing `## Hello World!` and see what happens. |
| 260 | + |
| 261 | +<section-live> |
| 262 | +<variable-string placeholder="write some notes here">notes</variable-string> |
| 263 | + |
| 264 | +```python |
| 265 | +display(Markdown(notes)) |
| 266 | +``` |
| 267 | + |
| 268 | +</section-live> |
| 269 | + |
| 270 | +#### Dropdown variables |
| 271 | + |
| 272 | +Dropdown allows options to be available in a dropdown list. To define the items |
| 273 | +used within the dropdown a Python list needs to be provided to the `items` html |
| 274 | +parameter. See below for how this works in practice. |
| 275 | + |
| 276 | +<variable-string label="Your own machine name" placeholder="Write a custom machine name here"> |
| 277 | + custom_machine |
| 278 | +</variable-string> |
| 279 | + |
| 280 | +<section-live> |
| 281 | + |
| 282 | +<variable-dropdown items="[1234, 2345, 'George', custom_machine]">machine</variable-dropdown> |
| 283 | + |
| 284 | +```python |
| 285 | +print(machine) |
| 286 | +``` |
| 287 | + |
| 288 | +</section-live> |
| 289 | + |
| 290 | +### Button sections |
| 291 | + |
| 292 | +Button groups are designed for long running or standalone tasks that |
| 293 | +should not run whenever a user changes a variable. |
| 294 | + |
| 295 | +They are defined as following: |
| 296 | + |
| 297 | +<variable-string>notes</variable-string> |
| 298 | + |
| 299 | +<section-button> |
| 300 | + |
| 301 | +```python |
| 302 | +display(Markdown(notes)) |
| 303 | +``` |
| 304 | + |
| 305 | +</section-button> |
| 306 | + |
| 307 | +They will not run until their respective button is pressed. |
| 308 | + |
| 309 | +#### Button customistion |
| 310 | + |
| 311 | +Button sections are customisable, their content can be changed to words by |
| 312 | +changing the value property. |
| 313 | + |
| 314 | +<section-button value="Submit"> |
| 315 | + |
| 316 | +```python |
| 317 | +submit_count += 1 |
| 318 | +display(Markdown('Submitted {} times!'.format(submit_count))) |
| 319 | +``` |
| 320 | + |
| 321 | +</section-button> |
| 322 | + |
| 323 | +Buttons can also be disabled using the conditional property. An example is the |
| 324 | +following button which is only enabled once the submit count becomes at least |
| 325 | +10. |
| 326 | + |
| 327 | +<section-button inline value="Super Submit" conditional="submit_count >= 10"> |
| 328 | + |
| 329 | +```python |
| 330 | +display(Markdown('## Super Submit!!')) |
| 331 | +``` |
| 332 | + |
| 333 | +</section-button> |
| 334 | + |
| 335 | +<section-button value="Slow button"> |
| 336 | + |
| 337 | +```python |
| 338 | +time.sleep(1) |
| 339 | +``` |
| 340 | + |
| 341 | +</section-button> |
| 342 | + |
| 343 | +### Output sections |
| 344 | + |
| 345 | +Code placed within output groups will run after any variable on the form |
| 346 | +changes in value. |
| 347 | + |
| 348 | +<section-output> |
| 349 | + |
| 350 | +```python |
| 351 | +output_count += 1 |
| 352 | +print(output_count) |
| 353 | + |
| 354 | +print(_scriptedforms_variable_handler.variables_json) |
| 355 | +``` |
| 356 | + |
| 357 | +</section-output> |
0 commit comments