JuliaGraphics / Immerse.jl

Dive deeper into your data with interactive graphics

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Subplots in Plots.jl

tbreloff opened this issue · comments

Hi Tim. I'm working on supporting this in Plots.jl. I'm super close to finishing, I'm just trying to get the display commands correct. If I have a Gadfly.Plot object, then it seems straightforward to update a plot and re-display it to the same canvas. However when I try to update a subplot (built using Compose.hstack and Compose.vstack, it always creates a new window.

So my question... if I have a Immerse.Figure object and a stale Compose.Context (which needs to be re-drawn), what do I need to do to update the context and figure? (but don't create a new figure)

I haven't tried subplots yet, unfortunately. Can you give me a quick test script that demonstrates the problem you're seeing?

Or a (potentially) better solution... how might I create a window with many canvases (one per plot) in a grid, similar to vstack([hstack(row...) for row in rows]...). I assume there's a straighforward construct with Gtk? I just haven't used Gtk much.

Yes, you can definitely do that---Gtk has sophisticated layout machinery (https://developer.gnome.org/gtk3/stable/ch29s02.html). I've wondered about that as well. The pluses are that you could align the canvases with other entities, like Buttons, popups, etc. However, you might want to render the "content" of the graphic inside one canvas, and the axis labels, ticks, etc, in an "outer" canvas. It seems that you can't have two canvases occupy the same grid cells (https://developer.gnome.org/gtk3/stable/GtkGrid.html, search for "several children").

See the test/gui.jl script for all kinds of useful demos for working with Gtk.

Reading your initial message more carefully, this code is perhaps what you're after? If you say

f.cc = Compose.vstack(...)

then it should render them all. Not sure how well that will work however.

While it's untested, I definitely intended for it to be possible to have multiple Figures per window. Something like

vbox = @Box(:v)
c1 = @Canvas()
c2 = @Canvas()
push!(vbox, c1)
push!(vbox, c2)
win = @Window(vbox, "Two figures")
showall(win)

and then set c1 and c2 as the canvases for two Figures.

Back to my initial attempt. This is what I do:

julia> using Plots; immerse!()
INFO: Recompiling stale cache file /home/tom/.julia/lib/v0.4/Plots.ji for module Plots.
[Plots.jl] Default backend: immerse
Plots.ImmersePackage()

julia> subplot(rand(100,4); n=4)
[Plots.jl] Initializing package: immerse
WARNING: Method definition display(Base.REPL.REPLDisplay{R<:Base.REPL.AbstractREPL}, Base.Multimedia.MIME{:text/html}, Gadfly.Plot) in module Gadfly at /home/tom/.julia/v0.4/Gadfly/src/Gadfly.jl:1033 overwritten in module DisplayGadfly at /home/tom/.julia/v0.4/Immerse/src/display_gadfly.jl:116.
WARNING: Method definition draw(Compose.Backend, Compose.Context) in module Compose at /home/tom/.julia/v0.4/Compose/src/container.jl:404 overwritten in module ImmerseCompose at /home/tom/.julia/v0.4/Immerse/src/compose.jl:33.
[Plots.jl] done.
disp2
Creating immerse figure: Dict{Any,Any}(:markersize=>3,:width=>1,:legend=>true,:color=>:auto,:title=>"",:xticks=>true,:yticks=>true,:fillto=>nothing,:windowtitle=>"Plots.jl",:heatmap_c=>(0.15,0.5),:size=>(600,400),:markercolor=>:match,:marker=>:none,:nbins=>100,:xlabel=>"",:args=>Any[],:label=>"AUTO",:ylabel=>"",:background_color=>RGB{U8}(1.0,1.0,1.0),:kwargs=>Any[],:show=>true,:yrightlabel=>"",:axis=>:left,:linetype=>:line,:linestyle=>:solid,:reg=>false,:n=>4)

julia> subplot!(rand(100,4))
disp2

The first call to subplot pops up a 2x2 subplotted grid in an Immerse window, just as expected (nice work on that, by the way!) Then the second call should update that canvas in place, by adding a new series to each subplot. The problem is that the Context doesn't get updated.

The same commands work well for plot and plot!, but I'm doing display(Immerse.Figure(fig.canvas, gplt)), where fig is a Immerse.Figure and gplt is a Gadfly.Plot. You do the redrawing within the Figure constructor when passed with a Plot. However I'm not sure the right commands to use to redraw the Context in the subplot case.

Hopefully this made sense! You can always look at my code. Thanks!

I think I figured it out... I just rebuild the Context from scratch on display or savepng and it works. Examples seem to match Gadfly, so success! Thanks for your help!

I'd still like to try the 2nd method (many widgets, each with 1 plot), but I'll close this issue now.

Awesome. Glad you figured it out with so little 😄 help from me.

Here's a demo of the other approach:

using Immerse, Gtk.ShortNames, ColorTypes

win = @Window()
vbox = @Box(:v)
c1 = @Canvas()
push!(vbox, c1)
c2 = @Canvas()
push!(vbox, c2)
push!(win, vbox)
f1 = Figure(c1, plot(x=rand(5), y=rand(5), Geom.point, Theme(background_color=colorant"white")))
x = linspace(0,4pi,101)
f2 = Figure(c2, plot(x=x, y=sin(x), Geom.line, Theme(background_color=colorant"white")))
showall(win)
setproperty!(c1, :expand, true)       # this might not have been obvious, and it doesn't work without it
setproperty!(c2, :expand, true)
display(f1)
display(f2)

Thanks... this is super helpful. I'll probably replace the Box with Paned (any gotchas I should watch out for with that?) My GUI background is in Qt. I've build several trading systems using Qt with both C++ and Python, so I expect the transition to Gtk to be both easy and confusing.

I started to write a request for additional Figure constructors, but I'll try to submit a PR instead... Hopefully you don't mind.

The issue, so you're aware, is that I don't want to create and pop-up a new window... I want to get a reference to the GtkBox that holds the buttons and canvas and add that to my subplot panes. To that end, I'll try to minimally change the figure() method to avoid window creation and to pass back the Box.

I'll probably replace the Box with Paned (any gotchas I should watch out for with that?)

Just the limitation to 2. There's also Grid which allows more complex geometries.

I started to write a request for additional Figure constructors, but I'll try to submit a PR instead... Hopefully you don't mind.

You must be joking. Thrilled, is more like it. Part of the reason I deposited this in JuliaGraphics was to abdicate ownership from the beginning.

I want to get a reference to the GtkBox that holds the buttons and canvas and add that to my subplot panes.

Agreed, that's important. I don't mind surgery (small or large)---the end result should be easy to use but flexible enough to handle the diversity of tasks required. I think we're OK now on the "easy" part but definitely not on the "sufficiently flexible" part. So I heartily approve extensions along these lines.

Ok great to hear. I do strive for code cleanliness as much as possible, mainly for my own sake because I hate reading through messy/verbose code (especially if it's my own!) I think I can split the creation of the Box into a separate function without much/any change to the current interface.