7

Plots is simple and powerful but sometimes I would like to have a little bit more control over individual elements of the plot to fine-tune its appearance.

Is it possible to update the plot object of the backend directly?

E.g., for the default pyplot backend, I tried

using Plots
p = plot(sin)
p.o[:axes][1][:xaxis][:set_ticks_position]("top")

but the plot does not change. Calling p.o[:show]() afterwards does not help, either.

In other words: Is there a way to use the PyPlot interface for a plot that was initially created with Plots?

Edit:

The changes to the PyPlot object become visible (also in the gui) when saving the figure:

using Plots
using PyPlot
p = Plots.plot(sin, top_margin=1cm)
gui() # not needed when using the REPL
gca()[:xaxis][:set_ticks_position]("top")
PyPlot.savefig("test.png")

Here, I used p.o[:axes][1] == gca(). One has to set top_margin=1cm because the plot area is not adjusted automatically (for my actual fine-tuning, this doesn't matter).

This also works for subsequent updates as long as only the PyPlot interface is used. E.g., after the following commands, the plot will have a red right border in addition to labels at the top:

gca()[:spines]["right"][:set_color]("red")
PyPlot.savefig("test.png")

However, when a Plots command like plot!(xlabel="foo") is used, all previous changes made with PyPlot are overwritten (which is not suprising).

The remaining question is how to update the gui interactively without having to call PyPlot.savefig explicitly.

tim
  • 2,076
  • 8
  • 14

2 Answers2

1

No - the plot is a Plots object, not a PyPlot object. In your specific example you can do plot(sin, xmirror = true).

Michael K. Borregaard
  • 7,864
  • 1
  • 28
  • 35
  • But you can edit a Plots object directly, using Plots notation. In this case `p[1][:xaxis][:mirror] = true; display(p)` . `display()` is essential for any updates of the plot to show. – Michael K. Borregaard Mar 10 '17 at 14:23
  • I understand that `p` is a `Plots` object, but it seems to wrap an at least partially functional `PyPlot` object in `p.o`, see my updated question for a workaround. Of course, one should use the `Plots` interface if possible, like in this example, but sometimes I know only how to make a specific change in `PyPlot`. Therefore, I would appreciate a way to completely hand over control to `PyPlot` (when using the `pyplot` backend) after 90% of the plot is done in `Plots`. – tim Mar 14 '17 at 14:48
  • Very impressive hacking :-) – Michael K. Borregaard Mar 14 '17 at 18:52
1

I'm trying to do the same but didn't find a solution to update an existing plot. But here is a partial answer: you can query information from the PyPlot axes object

julia> Plots.plot(sin, 1:4)

julia> Plots.PyPlot.plt[:xlim]()
(1.0,4.0)

julia> Plots.plot(sin, 20:24)

julia> ax = Plots.PyPlot.plt[:xlim]()
(20.0,24.0)

and it gets updated.

Fred Schoen
  • 1,372
  • 13
  • 18
  • Interesting. BTW you don't need Plots in front of PyPlot. – Michael K. Borregaard Mar 10 '17 at 13:55
  • But I'd be very surprised if this can be used to do anything to edit the plot. – Michael K. Borregaard Mar 10 '17 at 23:22
  • And I didn't manage to edit the plot. I looked through `Plots.jl` source code for a while and just couldn't figure out why `Plots` updates the plot correctly but why I couldn't sneak it. I guess my julia skills aren't good enough, I should have used the debugger to see what actually is called. – Fred Schoen Mar 14 '17 at 10:50