-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support subplot creation at runtime via createGrid
#45
Merged
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
fc62e88
move impl of `plotly.nim` to `plotly_display` to make it importable
Vindaar fcc0914
add `createGrid` to create a grid subplot at runtime
Vindaar a7cabcf
export `showImpl` plot to get `PlotJson` from grid
Vindaar d4bb016
fix JS backend
Vindaar c53fcdc
only add plots, which have been assigned, i.e. not nil
Vindaar c790be7
add bounds check for `[]=` at grid index
Vindaar 6eeb7ef
fix calculation of grid size if numPlotsPerRow > 0
Vindaar 4463cda
add creation and assignment of `Grid` based on row, col tuples
Vindaar 5cd526d
remove left over JS related code from plotly_display
Vindaar 7726df7
actually run the subplots example with nimble test
Vindaar 7527d13
fix assignment coord tuple in grid example using
Vindaar 857121a
rename `showImpl` to `toPlotJson`
Vindaar c095080
only import display in subplots if not JS target
Vindaar 9311dd4
import types in plotly_js to get `parseTraces` working
Vindaar File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
import strutils | ||
import os | ||
import json | ||
import sequtils | ||
|
||
# we now import the plotly modules and export them so that | ||
# the user sees them as a single module | ||
import api, plotly_types, plotly_subplots | ||
|
||
when defined(webview): | ||
import webview | ||
|
||
# normally just import browsers module. Howver, in case we run | ||
# tests on travis, we need a way to open a browser, which is | ||
# non-blocking. For some reason `xdg-open` does not return immediately | ||
# on travis. | ||
when not defined(travis): | ||
import browsers | ||
|
||
proc showPlot(file: string) = | ||
when defined(travis): | ||
# patched version of Nim's `openDefaultBrowser` which always | ||
# returns immediately | ||
var u = quoteShell(file) | ||
discard execShellCmd("xdg-open " & u & " &") | ||
elif defined(webview): | ||
let w = newWebView("Nim Plotly", "file://" & file) | ||
w.run() | ||
w.exit() | ||
else: | ||
# default normal browser | ||
openDefaultBrowser(file) | ||
|
||
include plotly/tmpl_html | ||
|
||
# check whether user is compiling with thread support. We can only compile | ||
# `saveImage` if the user compiles with it! | ||
const hasThreadSupport* = compileOption("threads") | ||
when hasThreadSupport: | ||
import threadpool | ||
import plotly/image_retrieve | ||
|
||
proc parseTraces*[T](traces: seq[Trace[T]]): string = | ||
## parses the traces of a Plot object to strings suitable for | ||
## plotly by creating a JsonNode and converting to string repr | ||
result.toUgly(% traces) | ||
|
||
# `show` and `save` are only used for the C target | ||
proc fillImageInjectTemplate(filetype, width, height: string): string = | ||
## fill the image injection code with the correct fields | ||
## Here we use numbering of elements to replace in the template. | ||
# Named replacements don't seem to work because of the characters | ||
# around the `$` calls | ||
result = injectImageCode % [filetype, | ||
filetype, | ||
width, | ||
height, | ||
filetype, | ||
width, | ||
height] | ||
|
||
proc fillHtmlTemplate(html_template, | ||
data_string: string, | ||
p: SomePlot, | ||
filename = ""): string = | ||
## fills the HTML template with the correct strings and, if compiled with | ||
## ``--threads:on``, inject the save image HTML code and fills that | ||
var | ||
slayout = "{}" | ||
title = "" | ||
if p.layout != nil: | ||
when type(p) is Plot: | ||
slayout = $(%p.layout) | ||
title = p.layout.title | ||
else: | ||
slayout = $p.layout | ||
title = p.layout{"title"}.getStr | ||
|
||
# read the HTML template and insert data, layout and title strings | ||
# imageInject is will be filled iff the user compiles with ``--threads:on`` | ||
# and a filename is given | ||
var imageInject = "" | ||
when hasThreadSupport: | ||
if filename.len > 0: | ||
# prepare save image code | ||
let filetype = parseImageType(filename) | ||
when type(p) is Plot: | ||
let swidth = $p.layout.width | ||
let sheight = $p.layout.height | ||
else: | ||
let swidth = $p.layout{"width"} | ||
let sheight = $p.layout{"height"} | ||
imageInject = fillImageInjectTemplate(filetype, swidth, sheight) | ||
|
||
# now fill all values into the html template | ||
result = html_template % ["data", data_string, "layout", slayout, | ||
"title", title, "saveImage", imageInject] | ||
|
||
proc save*(p: SomePlot, path = "", html_template = defaultTmplString, filename = ""): string = | ||
result = path | ||
if result == "": | ||
when defined(Windows): | ||
result = getEnv("TEMP") / "x.html" | ||
else: | ||
result = "/tmp/x.html" | ||
|
||
when type(p) is Plot: | ||
# convert traces to data suitable for plotly and fill Html template | ||
let data_string = parseTraces(p.traces) | ||
else: | ||
let data_string = $p.traces | ||
let html = html_template.fillHtmlTemplate(data_string, p, filename) | ||
|
||
var | ||
f: File | ||
if not open(f, result, fmWrite): | ||
quit "could not open file for json" | ||
f.write(html) | ||
f.close() | ||
|
||
when not hasThreadSupport: | ||
# some violation of DRY for the sake of better error messages at | ||
# compile time | ||
proc show*(p: SomePlot, | ||
filename: string, | ||
path = "", | ||
html_template = defaultTmplString) = | ||
{.fatal: "`filename` argument to `show` only supported if compiled " & | ||
"with --threads:on!".} | ||
|
||
proc show*(p: SomePlot, path = "", html_template = defaultTmplString) = | ||
## creates the temporary Html file using `save`, and opens the user's | ||
## default browser | ||
let tmpfile = p.save(path, html_template) | ||
|
||
showPlot(tmpfile) | ||
sleep(1000) | ||
## remove file after thread is finished | ||
removeFile(tmpfile) | ||
|
||
proc saveImage*(p: SomePlot, filename: string) = | ||
{.fatal: "`saveImage` only supported if compiled with --threads:on!".} | ||
|
||
else: | ||
# if compiled with --threads:on | ||
proc show*(p: SomePlot, filename = "", path = "", html_template = defaultTmplString) = | ||
## creates the temporary Html file using `save`, and opens the user's | ||
## default browser | ||
# if we are handed a filename, the user wants to save the file to disk. | ||
# Start a websocket server to receive the image data | ||
var thr: Thread[string] | ||
if filename.len > 0: | ||
# wait a short while to make sure the server is up and running | ||
thr.createThread(listenForImage, filename) | ||
|
||
let tmpfile = p.save(path, html_template, filename) | ||
showPlot(tmpfile) | ||
if filename.len > 0: | ||
# wait for thread to join | ||
thr.joinThread | ||
removeFile(tmpfile) | ||
|
||
proc saveImage*(p: SomePlot, filename: string) = | ||
## saves the image under the given filename | ||
## supported filetypes: | ||
## - jpg, png, svg, webp | ||
## Note: only supported if compiled with --threads:on! | ||
p.show(filename = filename) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer a
rows, columns: int
and a[]
/[]=
that takes a row/column pair in order to better work with MxN subplotsThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally agree with you. That's what I intuitively would expect when dealing with a 2D grid anyways. @timotheecour your opinion?
I'm fine either way though.