Open XilinJia opened 6 years ago
For the moment: InspectDR only supports Plot.jl's layout functionnality when you save a figure. It does not support this feature on with the interactive GUI mode.
To save a figure, add the following line to your code:
savefig("outfile.png")
Though you simply specified a (# row, # column) format for your plot's layout, Plots.jl supports very arbitrary layouts. Sadly, this flexibility in layouts is not very conducive to the way the Gtk gridding widget works.
So for that reason, I have opted not to support the layout feature from the GUI mode.
The GUI is very much targeting interactive use - where presentation is not as important as making each plot easy to "explore".
In its GUI, InspectDR currently only plots row-by-row - but it does allow the user to change the amount of columns used when displaying plots.
To modify this number of columns, you must access the plot object directly:
p = plot(P1, P2, layout=(2,1), size=(1200,600))
mplot = p.o.mplot; #*** Get ref to plot object
mplot.layout.values[:ncolumns]=1 #Use 1 column only
display(InspectDR.GtkDisplay(), mplot) #Display directly from InspectDR - not Plots.jl
*** Note the use of ";" to supress show()
-ing mplot
- thus avoiding a current bug with the deprecated showcompact()
function.
I need the vertical layout (multiple rows of plots) because the data share the same x but with y values in different scales. Are there other ways of doing it?
I don't quite understand your words "InspectDR currently only plots row-by-row". As I can see, it only has one row of plots. I tried your workaround, but it doesn't seem to change anything on the plots.
The InspectDR GUI creates subplots from left-to-right, filling all slots in a row up until the maximum number of columns (given by mplot.layout.values[:ncolumns]
) is reached.
Once "ncolumns" is reached, InspectDR creates a new row, and starts filling that one from left-to-right, [repeat for other rows].
For example, plotting 4 subplots with values[:ncolumns] = 1
, you get:
1
2
3
4
Plotting 4 subplots with values[:ncolumns] = 2
, you get:
1 2
3 4
Plotting 4 subplots with values[:ncolumns] = 3
, you get:
1 2 3
4
@LeoK987: I tried your workaround, but it doesn't seem to change anything on the plots.
Did you succeed at saving a .png/.svg file using savefig("outfile.png")
? Did the exported image outfile.png
not have the correct layout?
You did not succeed at changing the # of columns using mplot.layout.values[:ncolumns]=1
? That's odd. Are you certain you displayed the plot using the display(InspectDR.GtkDisplay(), mplot)
command? The gui()
function would probably not work to display things properly.
InspectDR natively supports multiple y axes (called "strips"). You can see a few examples here:
You can find the code used to generate these plots under the sample directory:
The neat thing about using these y "strips" is that InspectDR always keeps the x axes of the strips tied together - even when interactively zooming with the mouse!!
Sadly, Plots.jl does not natively support this feature - so you would have to use InspectDR directly.
...but on the bright side, InspectDR runs much faster if you do not have to load Plots.jl (especially its time-to-first-plot).
PS Though I find the Plots.jl interface to be much better (simpler) when plotting from the command line, the InspectDR API is often adequate when you build scripts.
This is how I tried your workaround:
using Plots
inspectdr()
# gr()
function testPlot()
P1 = plot(Plots.fakedata(50,5))
P2 = plot(Plots.fakedata(50,5))
p = plot(P1, P2, layout=(2,1), size=(1200,600))
mplot = p.o.mplot; #*** Get ref to plot object
mplot.layout.values[:ncolumns]=1 #Use 1 column only
display(InspectDR.GtkDisplay(), mplot) #Display directly from InspectDR - not Plots.jl
gui()
end
I still get two plots in one row. Actually, I got two windows one behind the other. The plots are on the front window, and in the back window there are some text -(x,y) = (,)
. If I comment out the gui() line, then there is only the window with text.
Do I still need to save the figure here? If that is the case, then perhaps I don't want to do that because it defies the purpose of interactivity, doesn't it?
The strips feature sounds very neat. I was actually just looking at it. Perhaps I should adopt InspectDR directly in my script.
Correct. the gui()
command is not necessary (and you should not use it) when you display with display(InspectDR.GtkDisplay(), mplot)
.
Do I still need to save the figure here?
No. as you say, a saved image has zero-interactivity... but that's the thing about InspectDR at the moment. It is either supports layout, or interactivity (unless you use the vertically stacked "strips").
Note, however, that the image exported with savefig()
typically looks a bit better for publications, etc - because you have more control over the plot positions (ex: top plot could be made 2x higher than the bottom plot) - and the GUI widgets are not drawn.
I think most people would really like the strips feature. I wish all plotting packages had this. I find I often want to use this pattern to display plots - and that plotting apps are much easier to use if they support strips natively.
PLUS: The neat thing about the strips in InspectDR is that every subplot can be drawn with strips. So, for example, you can easily generate a "multi-plot" window where plots sample/demo5.jl
& sample/demo7.jl
are shown side-by-side... and the interactive GUI will work perfectly!
FYI
Very sorry. I tried your snippet above and saw your problem. Here is the solution:
using Plots
backend(:inspectdr)
function testPlot()
P1 = plot(Plots.fakedata(50,5));
P2 = plot(Plots.fakedata(50,5));
p = plot(P1, P2, layout=(2,1), size=(1200,600));
Plots.prepare_output(p)
mplot = p.o.mplot
mplot.layout.values[:ncolumns]=1 #Use 1 column only
display(InspectDR.GtkDisplay(), mplot) #Display directly from InspectDR - not Plots.jl
end
Note that the line Plots.prepare_output(p)
is needed to get Plots.jl to trigger InspectDR to generate the mplot
object. After that, you can overwrite value[:ncolumns]
& display.
...And I just realize you can make the process less invasive (use more of Plots.jl's normal flow) by using the following:
function testPlot()
P1 = plot(Plots.fakedata(50,5))
P2 = plot(Plots.fakedata(50,5))
plot(P1, P2, layout=(2,1), size=(1200,600))
p = gui()
p.src.layout.values[:ncolumns]=1 #Use 1 column only
InspectDR.refresh(p)
return p
end
Note that you might see glitch using this "flow" - because Plots.jl first displays the plot with 2 columns, then we overwrite that setting & refresh.
I wanted to send you a minimum working example for using InspectDR directly:
using InspectDR
using Colors
function testPlot2()
y1 = rand(50,5)
y2 = rand(50,5)
x = collect(1.0:size(y1,1))
add = InspectDR.add #Alias
line = InspectDR.line #Alias
COLOR_RED = RGB24(1, 0, 0)
COLOR_GREEN = RGB24(0, 1, 0)
COLOR_BLUE = RGB24(0, 0, 1)
COLOR_YELLOW = RGB24(1, 1, 0)
COLOR_CYAN = RGB24(0, 1, 1)
clist = [COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_CYAN]
#Using Plot2D simplified "template" constructor:
p = InspectDR.Plot2D(:lin, [:lin, :lin],
title = "Some Fake Data", xlabel = "X",
ylabels = ["Y1", "Y2"]
)
p.layout[:enable_legend] = true
for i in 1:size(y1,2)
wfrm = add(p, x, y1[:,i], id="y$i", strip=1)
wfrm.line = line(color=clist[i], width=2)
end
for i in 1:size(y2,2)
wfrm=add(p, x, y2[:,i], id="y$i", strip=2)
wfrm.line = line(color=clist[i], width=2)
end
gplot = display(InspectDR.GtkDisplay(), p)
return gplot
end
Note that this is significantly more verbose than what you get with Plots.jl. For example, you have to manually control the colors of your lines.
But it is much easier to explore the data afterwards given that the two x axes are tied together. The plots are also drawn in a slightly more compact fashion. See plot comparison below:
(Results from Plots.jl are on the left. On the right is an example of InspectDR's "strips".) Note the differences in default fonts & preset color schemes
Thanks so much for your kind guidance.
Another question: how to draw line segments?
I am probably not answering the right question, but here goes:
A line segment is typically drawn as a two-point dataset that represents {(x1, y1), (x2,y2)}
. You can add it to a plot (so that InspectDR draws it) using the same add
function as before:
xseg = [1, 10]; yseg = [2, 20]
wfrm = add(p, xseg, yseg, id="line segment", strip=1)
wfrm.line = line(color=clist[i], width=2) #Set line properties
...But something tells me this is not what you are asking.
Maybe you are asking how I added the H/V marker lines in demo7.jl (ie the dashed black lines)?: https://github.com/ma-laforge/FileRepo/blob/master/InspectDR/sampleplots/demo7.png
Please be more specific with your question if I have not answered it.
Sorry for not being very clear.
Say I have 4 points, each being defined as (xi, yi), and I want to connect Pts 1 and 2, and 3 and 4, but leave Pts 2 and 3 unconnected.
So I can draw Pts 1 and 2 with the statements you outlined above, and draw Pts 3 and 4 separately with the statements. Or are there better ways?
Well, just in case I what you really want is to create a coarser dashed line that what I currently support:
You can hack in a longer dashed line style by adding to the _setlinestyle()
function in src/cairo_ext.jl
. In case you don't know how to set the line style to dashed, you would simply add an argument to the line()
function:
wfrm.line = line(color=COLOR_BLUE, width=3, style=:dash)
If what you want to draw are actual line segments that start/stop at specific coordinates, you can embed NaN values in your vectors, for example:
xseg = [1, 10, NaN, 10, 20]; yseg = [2, 20, NaN, 10, 30]
That way, InspectDR can know which adjacent points are not connected.
Instead of getting two plots arranged vertically, got two horizontally.