Skip to content

Commit

Permalink
feat: implement functionality required for plotly multiple axes
Browse files Browse the repository at this point in the history
  • Loading branch information
slaclau committed Nov 6, 2024
1 parent 39e9504 commit de5d013
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 37 deletions.
60 changes: 32 additions & 28 deletions src/plotly_gtk/_chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,14 @@ def _draw_ticks(
tickvals = self.layout[axis]["_tickvals"]
ticktext = self.layout[axis]["_ticktext"]

font_dict = self.layout["font"]
font_dict["color"] = "#444"

if "font" in self.layout[axis]:
font_dict = update_dict(self.layout["font"], self.layout[axis]["font"])
else:
font_dict = self.layout["font"]
font_dict["color"] = "#444"
font_dict = update_dict(font_dict, self.layout[axis]["font"])
if "tickfont" in self.layout[axis]:
font_dict = update_dict(font_dict, self.layout[axis]["tickfont"])


context.set_source_rgb(*parse_color(font_dict["color"]))
font = parse_font(font_dict)
Expand Down Expand Up @@ -237,6 +240,8 @@ def _draw_axis(self, context, width, height, axis):
if "linecolor" not in self.layout[axis]:
return
context.set_source_rgb(*parse_color(self.layout[axis]["linecolor"]))
if DEBUG:
context.set_source_rgb(*parse_color("green"))

axis_letter = axis[0 : axis.find("axis")]
overlaying_axis = (
Expand All @@ -250,7 +255,8 @@ def _draw_axis(self, context, width, height, axis):
)
anchor_axis = (
"free"
if self.layout[axis]["anchor"] == "free"
if "anchor" not in self.layout[axis]
or self.layout[axis]["anchor"] == "free"
else (
self.layout[axis]["anchor"][0]
+ "axis"
Expand All @@ -264,7 +270,7 @@ def _draw_axis(self, context, width, height, axis):
)
position = (
self.layout[axis]["position"]
if anchor_axis == "free"
if "anchor" not in self.layout[axis] or anchor_axis == "free"
else (
self.layout[anchor_axis]["domain"][0]
if self.layout[axis]["side"] == "left"
Expand Down Expand Up @@ -339,29 +345,17 @@ def _calc_pos(
x_pos = []
y_pos = []

x_overlaying_axis = (
(xaxis["overlaying"][0] + "axis" + xaxis["overlaying"][1:])
if "overlaying" in xaxis
else ""
)
y_overlaying_axis = (
(yaxis["overlaying"][0] + "axis" + yaxis["overlaying"][1:])
if "overlaying" in yaxis
else ""
)

xdomain = (
xaxis["domain"]
if "overlaying" not in xaxis
else self.layout[x_overlaying_axis]["domain"]
)
ydomain = (
yaxis["domain"]
if "overlaying" not in yaxis
else self.layout[y_overlaying_axis]["domain"]
)

if xaxis is not None:
x_overlaying_axis = (
(xaxis["overlaying"][0] + "axis" + xaxis["overlaying"][1:])
if "overlaying" in xaxis
else ""
)
xdomain = (
xaxis["domain"]
if "overlaying" not in xaxis
else self.layout[x_overlaying_axis]["domain"]
)
xaxis_start = (
xdomain[0]
* (width - self.layout["_margin"]["l"] - self.layout["_margin"]["r"])
Expand All @@ -380,6 +374,16 @@ def _calc_pos(
) + xaxis_start

if yaxis is not None:
y_overlaying_axis = (
(yaxis["overlaying"][0] + "axis" + yaxis["overlaying"][1:])
if "overlaying" in yaxis
else ""
)
ydomain = (
yaxis["domain"]
if "overlaying" not in yaxis
else self.layout[y_overlaying_axis]["domain"]
)
yaxis_start = (
-(ydomain[0])
* (height - self.layout["_margin"]["t"] - self.layout["_margin"]["b"])
Expand Down
16 changes: 12 additions & 4 deletions src/plotly_gtk/chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ def _update_ranges(self):
if f"{axis_letter}axis" in plot
and plot[f"{axis_letter}axis"] == axis.replace("axis", "")
and ("visible" not in plot or plot["visible"])
and ("_visible" not in plot or plot["_visible"])
]
hidden_plots_on_axis = [
plot
Expand Down Expand Up @@ -464,6 +463,7 @@ def _update_layout(self):
),
margin=dict(autoexpand=True, t=100, l=80, r=80, b=80),
xaxis=dict(
anchor="y",
automargin=True,
autorange=True,
autotickangles=[0, 30, 90],
Expand Down Expand Up @@ -508,6 +508,7 @@ def _update_layout(self):
zerolinewidth=1,
),
yaxis=dict(
anchor="x",
automargin=True,
autorange=True,
autotickangles=[0, 30, 90],
Expand Down Expand Up @@ -569,7 +570,11 @@ def _update_layout(self):
)
else:
self.layout[xaxis]["_type"] = self.layout[xaxis]["type"]
if "side" in self.layout[xaxis] and self.layout[xaxis]["side"] == "top":
if (
"side" in self.layout[xaxis]
and self.layout[xaxis]["side"] == "top"
and "position" not in self.layout[xaxis]
):
self.layout[xaxis]["position"] = 1
template[xaxis] = template["xaxis"]
defaults[xaxis] = defaults["xaxis"]
Expand All @@ -583,15 +588,18 @@ def _update_layout(self):
if "yaxis" not in trace
or trace["yaxis"] == yaxis.replace("axis", "")
][0]
print(first_plot_on_axis)
self.layout[yaxis]["_type"] = (
self._detect_axis_type(first_plot_on_axis["y"])
if "y" in first_plot_on_axis
else "linear"
)
else:
self.layout[yaxis]["_type"] = self.layout[yaxis]["type"]
if "side" in self.layout[yaxis] and self.layout[yaxis]["side"] == "right":
if (
"side" in self.layout[yaxis]
and self.layout[yaxis]["side"] == "right"
and "position" not in self.layout[yaxis]
):
self.layout[yaxis]["position"] = 1
template[yaxis] = template["yaxis"]
defaults[yaxis] = defaults["yaxis"]
Expand Down
62 changes: 60 additions & 2 deletions src/plotly_gtk/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
multiple_axes_demos = [
"two_y_axes",
"multiple_y_axes_subplots",
# "multiple_axes",
"multiple_axes",
# "autoshift",
# "shift_by_pixels",
# "syncticks",
Expand Down Expand Up @@ -195,6 +195,64 @@ def _get_multiple_axes_test_figure(reference):
col=2,
secondary_y=True,
)
elif reference == "multiple_axes":
fig = go.Figure()

fig.add_trace(go.Scatter(x=[1, 2, 3], y=[4, 5, 6], name="yaxis1 data"))

fig.add_trace(
go.Scatter(x=[2, 3, 4], y=[40, 50, 60], name="yaxis2 data", yaxis="y2")
)

fig.add_trace(
go.Scatter(
x=[4, 5, 6], y=[40000, 50000, 60000], name="yaxis3 data", yaxis="y3"
)
)

fig.add_trace(
go.Scatter(
x=[5, 6, 7], y=[400000, 500000, 600000], name="yaxis4 data", yaxis="y4"
)
)

# Create axis objects
fig.update_layout(
xaxis=dict(domain=[0.3, 0.7]),
yaxis=dict(
title=dict(text="yaxis title", font=dict(color="#1f77b4")),
tickfont=dict(color="#1f77b4"),
),
yaxis2=dict(
title=dict(text="yaxis2 title", font=dict(color="#ff7f0e")),
tickfont=dict(color="#ff7f0e"),
anchor="free",
overlaying="y",
side="left",
position=0.15,
),
yaxis3=dict(
title=dict(text="yaxis3 title", font=dict(color="#d62728")),
tickfont=dict(color="#d62728"),
anchor="x",
overlaying="y",
side="right",
),
yaxis4=dict(
title=dict(text="yaxis4 title", font=dict(color="#9467bd")),
tickfont=dict(color="#9467bd"),
anchor="free",
overlaying="y",
side="right",
position=0.85,
),
)

# Update layout properties
fig.update_layout(
title_text="multiple y-axes example",
width=800,
)
else:
return
return fig
Expand All @@ -205,7 +263,7 @@ def test(app):
paned = Gtk.Paned()
window.set_content(paned)

fig = get_test_figure("multiple_y_axes_subplots")
fig = get_test_figure("multiple_axes")
print(fig)
# print(fig["layout"]["template"])

Expand Down
21 changes: 18 additions & 3 deletions src/plotly_gtk/widgets/axis_title.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,27 @@ def __init__(self, plot: "PlotlyGtk", axis: Spec, axis_name: str):
self.label.set_markup(axis["title"]["text"])
self.append(self.label)

anchor_axis = (
"free"
if "anchor" not in axis or axis["anchor"] == "free"
else (axis["anchor"][0] + "axis" + axis["anchor"][1:])
)
position = (
axis["position"]
if "anchor" not in axis or anchor_axis == "free"
else (
plot.layout[anchor_axis]["domain"][0]
if axis["side"] == "left" or axis["side"] == "bottom"
else plot.layout[anchor_axis]["domain"][-1]
)
)

if axis_letter == "x":
font_extra = (metrics.get_ascent() + metrics.get_descent()) / Pango.SCALE

orientation = "h"
x = (axis["domain"][0] + axis["domain"][-1]) / 2
y = axis["position"]
y = position
xanchor = "center"
yanchor = "top" if axis["side"] == "bottom" else "bottom"
xoffset = 0
Expand All @@ -56,7 +71,7 @@ def __init__(self, plot: "PlotlyGtk", axis: Spec, axis_name: str):
font_extra = max(layout.get_pixel_size()[0], font_extra)

orientation = "v"
x = axis["position"]
x = position
y = (axis["domain"][0] + axis["domain"][-1]) / 2
xanchor = "right" if axis["side"] == "left" else "left"
yanchor = "middle"
Expand All @@ -67,7 +82,7 @@ def __init__(self, plot: "PlotlyGtk", axis: Spec, axis_name: str):
xoffset = (
-standoff - ticklen - font_extra - x_size_error
if axis["side"] == "left"
else standoff + ticklen - font_extra + x_size_error
else standoff + ticklen + font_extra + x_size_error
)
yoffset = 0
angle = 270 # angle = 270 if axis["side"] == "left" else 90
Expand Down

0 comments on commit de5d013

Please sign in to comment.