-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
added fill_percent to params for insert, swap and remove #946
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
Changes from 7 commits
1b09e21
b149e0d
9705dde
c26b763
38f88ef
1991c7f
35716d6
8517f3f
3c0d337
8def60c
b091aaf
85d7549
f898892
2c07b12
7f08843
97213c9
8bde810
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,10 +15,11 @@ | |
|
||
IPython = optional_imports.get_module('IPython') | ||
|
||
# default HTML parameters | ||
MASTER_WIDTH = 400 | ||
MASTER_HEIGHT = 400 | ||
FONT_SIZE = 10 | ||
# default parameters for HTML preview | ||
MASTER_WIDTH = 500 | ||
MASTER_HEIGHT = 500 | ||
FONT_SIZE = 9 | ||
|
||
|
||
ID_NOT_VALID_MESSAGE = ( | ||
"Your box_id must be a number in your dashboard. To view a " | ||
|
@@ -44,9 +45,9 @@ def _box(fileId='', shareKey=None, title=''): | |
} | ||
return box | ||
|
||
|
||
def _container(box_1=None, box_2=None, size=MASTER_HEIGHT, | ||
sizeUnit='px', direction='vertical'): | ||
def _container(box_1=None, box_2=None, | ||
size=50, sizeUnit='%', | ||
direction='vertical'): | ||
if box_1 is None: | ||
box_1 = _empty_box() | ||
if box_2 is None: | ||
|
@@ -60,6 +61,7 @@ def _container(box_1=None, box_2=None, size=MASTER_HEIGHT, | |
'first': box_1, | ||
'second': box_2 | ||
} | ||
|
||
return container | ||
|
||
dashboard_html = (""" | ||
|
@@ -74,7 +76,7 @@ def _container(box_1=None, box_2=None, size=MASTER_HEIGHT, | |
</style> | ||
</head> | ||
<body> | ||
<canvas id="myCanvas" width="400" height="400"></canvas> | ||
<canvas id="myCanvas" width="{width}" height="{height}"></canvas> | ||
<script> | ||
var canvas = document.getElementById('myCanvas'); | ||
var context = canvas.getContext('2d'); | ||
|
@@ -91,17 +93,15 @@ def _container(box_1=None, box_2=None, size=MASTER_HEIGHT, | |
|
||
|
||
def _draw_line_through_box(dashboard_html, top_left_x, top_left_y, box_w, | ||
box_h, direction='vertical'): | ||
is_horizontal = (direction == 'horizontal') | ||
|
||
box_h, is_horizontal, direction, fill_percent=50): | ||
if is_horizontal: | ||
new_top_left_x = top_left_x + box_w / 2 | ||
new_top_left_x = top_left_x + box_w * (fill_percent / 100.) | ||
new_top_left_y = top_left_y | ||
new_box_w = 1 | ||
new_box_h = box_h | ||
else: | ||
new_top_left_x = top_left_x | ||
new_top_left_y = top_left_y + box_h / 2 | ||
new_top_left_y = top_left_y + box_h * (fill_percent / 100.) | ||
new_box_w = box_w | ||
new_box_h = 1 | ||
|
||
|
@@ -120,13 +120,13 @@ def _draw_line_through_box(dashboard_html, top_left_x, top_left_y, box_w, | |
return dashboard_html | ||
|
||
|
||
def _add_html_text(dashboard_html, text, top_left_x, top_left_y, box_w, box_h): | ||
def _add_html_text(dashboard_html, text, top_left_x, top_left_y, box_w, | ||
box_h): | ||
html_text = """<!-- Insert box numbers --> | ||
context.font = '{font_size}pt Times New Roman'; | ||
context.font = '{}pt Times New Roman'; | ||
context.textAlign = 'center'; | ||
context.fillText({text}, {top_left_x} + 0.5*{box_w}, {top_left_y} + 0.5*{box_h}); | ||
""".format(text=text, top_left_x=top_left_x, top_left_y=top_left_y, | ||
box_w=box_w, box_h=box_h, font_size=FONT_SIZE) | ||
context.fillText({}, {} + 0.5*{}, {} + 0.5*{}); | ||
""".format(FONT_SIZE, text, top_left_x, box_w, top_left_y, box_h) | ||
|
||
index_to_add_text = dashboard_html.find('</script>') - 1 | ||
dashboard_html = (dashboard_html[:index_to_add_text] + html_text + | ||
|
@@ -212,8 +212,6 @@ def __init__(self, content=None): | |
self['version'] = content['version'] | ||
self['settings'] = content['settings'] | ||
|
||
self._set_container_sizes() | ||
|
||
def _compute_box_ids(self): | ||
box_ids_to_path = {} | ||
all_nodes = list(node_generator(self['layout'])) | ||
|
@@ -260,24 +258,6 @@ def _make_all_nodes_and_paths(self): | |
all_paths.remove(path_second) | ||
return all_nodes, all_paths | ||
|
||
def _set_container_sizes(self): | ||
if self['layout'] is None: | ||
return | ||
|
||
all_nodes, all_paths = self._make_all_nodes_and_paths() | ||
|
||
# set dashboard_height proportional to max_path_len | ||
max_path_len = max(len(path) for path in all_paths) | ||
dashboard_height = 500 + 250 * max_path_len | ||
self['layout']['size'] = dashboard_height | ||
self['layout']['sizeUnit'] = 'px' | ||
|
||
for path in all_paths: | ||
if len(path) != 0: | ||
if self._path_to_box(path)['type'] == 'split': | ||
self._path_to_box(path)['size'] = 50 | ||
self._path_to_box(path)['sizeUnit'] = '%' | ||
|
||
def _path_to_box(self, path): | ||
loc_in_dashboard = self['layout'] | ||
for first_second in path: | ||
|
@@ -325,17 +305,17 @@ def get_preview(self): | |
elif self['layout'] is None: | ||
return IPython.display.HTML(dashboard_html) | ||
|
||
x = 0 | ||
y = 0 | ||
top_left_x = 0 | ||
top_left_y = 0 | ||
box_w = MASTER_WIDTH | ||
box_h = MASTER_HEIGHT | ||
html_figure = dashboard_html | ||
box_ids_to_path = self._compute_box_ids() | ||
# used to store info about box dimensions | ||
path_to_box_specs = {} | ||
first_box_specs = { | ||
'top_left_x': x, | ||
'top_left_y': y, | ||
'top_left_x': top_left_x, | ||
'top_left_y': top_left_y, | ||
'box_w': box_w, | ||
'box_h': box_h | ||
} | ||
|
@@ -351,57 +331,64 @@ def get_preview(self): | |
current_box_specs = path_to_box_specs[path] | ||
|
||
if self._path_to_box(path)['type'] == 'split': | ||
html_figure = _draw_line_through_box( | ||
html_figure, | ||
current_box_specs['top_left_x'], | ||
current_box_specs['top_left_y'], | ||
current_box_specs['box_w'], | ||
current_box_specs['box_h'], | ||
direction=self._path_to_box(path)['direction'] | ||
) | ||
fill_percent = self._path_to_box(path)['size'] | ||
direction = self._path_to_box(path)['direction'] | ||
is_horizontal = (direction == 'horizontal') | ||
|
||
# determine the specs for resulting two boxes from split | ||
is_horizontal = ( | ||
self._path_to_box(path)['direction'] == 'horizontal' | ||
) | ||
x = current_box_specs['top_left_x'] | ||
y = current_box_specs['top_left_y'] | ||
top_left_x = current_box_specs['top_left_x'] | ||
top_left_y = current_box_specs['top_left_y'] | ||
box_w = current_box_specs['box_w'] | ||
box_h = current_box_specs['box_h'] | ||
|
||
html_figure = _draw_line_through_box( | ||
html_figure, top_left_x, top_left_y, box_w, box_h, | ||
is_horizontal=is_horizontal, direction=direction, | ||
fill_percent=fill_percent | ||
) | ||
|
||
# determine the specs for resulting two box split | ||
if is_horizontal: | ||
new_box_w = box_w / 2 | ||
new_top_left_x = top_left_x | ||
new_top_left_y = top_left_y | ||
new_box_w = box_w * (fill_percent / 100.) | ||
new_box_h = box_h | ||
new_top_left_x = x + box_w / 2 | ||
new_top_left_y = y | ||
|
||
new_top_left_x_2 = top_left_x + new_box_w | ||
new_top_left_y_2 = top_left_y | ||
new_box_w_2 = box_w * ((100 - fill_percent) / 100.) | ||
new_box_h_2 = box_h | ||
else: | ||
new_top_left_x = top_left_x | ||
new_top_left_y = top_left_y | ||
new_box_w = box_w | ||
new_box_h = box_h / 2 | ||
new_top_left_x = x | ||
new_top_left_y = y + box_h / 2 | ||
new_box_h = box_h * (fill_percent / 100.) | ||
|
||
box_1_specs = { | ||
'top_left_x': x, | ||
'top_left_y': y, | ||
new_top_left_x_2 = top_left_x | ||
new_top_left_y_2 = (top_left_y + | ||
box_h * (fill_percent / 100.)) | ||
new_box_w_2 = box_w | ||
new_box_h_2 = box_h * ((100 - fill_percent) / 100.) | ||
|
||
first_box_specs = { | ||
'top_left_x': top_left_x, | ||
'top_left_y': top_left_y, | ||
'box_w': new_box_w, | ||
'box_h': new_box_h | ||
} | ||
box_2_specs = { | ||
'top_left_x': new_top_left_x, | ||
'top_left_y': new_top_left_y, | ||
'box_w': new_box_w, | ||
'box_h': new_box_h | ||
second_box_specs = { | ||
'top_left_x': new_top_left_x_2, | ||
'top_left_y': new_top_left_y_2, | ||
'box_w': new_box_w_2, | ||
'box_h': new_box_h_2 | ||
} | ||
|
||
path_to_box_specs[path + ('first',)] = box_1_specs | ||
path_to_box_specs[path + ('second',)] = box_2_specs | ||
path_to_box_specs[path + ('first',)] = first_box_specs | ||
path_to_box_specs[path + ('second',)] = second_box_specs | ||
|
||
elif self._path_to_box(path)['type'] == 'box': | ||
for box_id in box_ids_to_path: | ||
if box_ids_to_path[box_id] == path: | ||
number = box_id | ||
|
||
html_figure = _add_html_text( | ||
html_figure, number, | ||
path_to_box_specs[path]['top_left_x'], | ||
|
@@ -413,7 +400,7 @@ def get_preview(self): | |
# display HTML representation | ||
return IPython.display.HTML(html_figure) | ||
|
||
def insert(self, box, side='above', box_id=None): | ||
def insert(self, box, side='above', box_id=None, fill_percent=50): | ||
""" | ||
Insert a box into your dashboard layout. | ||
|
||
|
@@ -423,7 +410,14 @@ def insert(self, box, side='above', box_id=None): | |
'left', and 'right'. | ||
:param (int) box_id: the box id which is used as the reference box for | ||
the insertion of the box. | ||
|
||
:param (float) fill_percent: specifies the percentage of the container | ||
box from the given 'side' that the new box occupies. For example | ||
if you apply the method\n | ||
.insert(box=new_box, box_id=2, side='left', fill_percent=20)\n | ||
to a dashboard object, a new box is inserted 20% from the left | ||
side of the box with id #2. Run .get_preview() to see the box ids | ||
assigned to each box in the dashboard layout. | ||
Default = 50 | ||
Example: | ||
``` | ||
import plotly.dashboard_objs as dashboard | ||
|
@@ -440,7 +434,7 @@ def insert(self, box, side='above', box_id=None): | |
my_dboard.insert(box_1, 'left', 1) | ||
my_dboard.insert(box_1, 'below', 2) | ||
my_dboard.insert(box_1, 'right', 3) | ||
my_dboard.insert(box_1, 'above', 4) | ||
my_dboard.insert(box_1, 'above', 4, fill_percent=30) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does this output for the example look correct to you @Kully ? Not sure There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Kully running on your branch in python 2.7 I get the desired output but in python 3.6 I get: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wow, that's interesting - |
||
|
||
my_dboard.get_preview() | ||
``` | ||
|
@@ -449,7 +443,9 @@ def insert(self, box, side='above', box_id=None): | |
|
||
# doesn't need box_id or side specified for first box | ||
if self['layout'] is None: | ||
self['layout'] = _container(box, _empty_box()) | ||
self['layout'] = _container( | ||
box, _empty_box(), size=MASTER_HEIGHT, sizeUnit='px' | ||
) | ||
else: | ||
if box_id is None: | ||
raise exceptions.PlotlyError( | ||
|
@@ -458,28 +454,38 @@ def insert(self, box, side='above', box_id=None): | |
) | ||
if box_id not in box_ids_to_path: | ||
raise exceptions.PlotlyError(ID_NOT_VALID_MESSAGE) | ||
|
||
if fill_percent < 0 or fill_percent > 100: | ||
raise exceptions.PlotlyError( | ||
'fill_percent must be a number between 0 and 100 ' | ||
'inclusive' | ||
) | ||
if side == 'above': | ||
old_box = self.get_box(box_id) | ||
self._insert( | ||
_container(box, old_box, direction='vertical'), | ||
_container(box, old_box, direction='vertical', | ||
size=fill_percent), | ||
box_ids_to_path[box_id] | ||
) | ||
elif side == 'below': | ||
old_box = self.get_box(box_id) | ||
self._insert( | ||
_container(old_box, box, direction='vertical'), | ||
_container(old_box, box, direction='vertical', | ||
size=100 - fill_percent), | ||
box_ids_to_path[box_id] | ||
) | ||
elif side == 'left': | ||
old_box = self.get_box(box_id) | ||
self._insert( | ||
_container(box, old_box, direction='horizontal'), | ||
_container(box, old_box, direction='horizontal', | ||
size=fill_percent), | ||
box_ids_to_path[box_id] | ||
) | ||
elif side == 'right': | ||
old_box = self.get_box(box_id) | ||
self._insert( | ||
_container(old_box, box, direction='horizontal'), | ||
_container(old_box, box, direction='horizontal', | ||
size =100 - fill_percent), | ||
box_ids_to_path[box_id] | ||
) | ||
else: | ||
|
@@ -489,8 +495,6 @@ def insert(self, box, side='above', box_id=None): | |
"'above', 'below', 'left', and 'right'." | ||
) | ||
|
||
self._set_container_sizes() | ||
|
||
def remove(self, box_id): | ||
""" | ||
Remove a box from the dashboard by its box_id. | ||
|
@@ -530,8 +534,6 @@ def remove(self, box_id): | |
else: | ||
self['layout'] = None | ||
|
||
self._set_container_sizes() | ||
|
||
def swap(self, box_id_1, box_id_2): | ||
""" | ||
Swap two boxes with their specified ids. | ||
|
@@ -580,5 +582,3 @@ def swap(self, box_id_1, box_id_2): | |
for first_second in pairs[0][:-1]: | ||
loc_in_dashboard = loc_in_dashboard[first_second] | ||
loc_in_dashboard[pairs[0][-1]] = pairs[1] | ||
|
||
self._set_container_sizes() |
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 would say this is more of added functionality than a bug fix (do you agree?) so I think it should be version 2.5.0 based on: https://semver.org/
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 agree they are not bug fixes. The question I have now about my message below is if we consider these points as backwards-compatible. Functionally the API is the same and produces dashboards that look the same. I just had to modify a test or two.
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.
Is there no way to add this functionality as an option rather than the default so that it remains backwards compatible?
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 don't think it's worth it. There was a technical issue with, before, using pixels as the metric for slicing up the boxes (since it was always in half) but the new param uses percentage to insert the boxes next to a pre-existing box
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.
ok got it 👍
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.
@Kully did you get a chance to update this to
2.5.0
and updateplotly/version.py
as well?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.
no thank you for reminding me