Last active
December 29, 2019 02:22
-
-
Save caseykneale/49e447f41427cfdcc1efbd681c8f6833 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Gtk, Plots, Cairo | |
using DataStructures | |
function replaceBiggest!(a,b) | |
selinds = abs.(a) .< abs.(b) | |
a[ selinds ] .= b[ selinds ] | |
end | |
mutable struct ClickablePlot | |
#plot::Plots.Plot#storing and mutating plots is expensive! | |
X_ref::Any #instead let's store a reference to the points in the plot... | |
Y_ref::Any | |
canvas::GtkCanvas | |
plotarea::Vector{Float64} | |
cairoimg::Cairo.CairoSurfaceBase{UInt32} | |
xplotlim::Vector{Float64} | |
yplotlim::Vector{Float64} | |
clickstate::UInt8 | |
uicoords::Queue{ Tuple{ Float64,Float64 } } | |
plotcoords::Queue{ Tuple{ Float64,Float64 } } | |
end | |
function ClickablePlot( plt::Plots.Plot, | |
x = plt.series_list[1].plotattributes[:x], | |
y = plt.series_list[1].plotattributes[:y] ) | |
#Find most extreme axis limits in all subplots | |
xplotlim, yplotlim = zeros(2), zeros(2) | |
for subplot in a.subplots | |
replaceBiggest!(xplotlim, Plots.axis_limits(subplot, :x)) | |
replaceBiggest!(yplotlim, Plots.axis_limits(subplot, :y)) | |
end | |
plot_max = maximum(Plots.gr_plot_size) | |
vp_plot_area_px = Plots.viewport_plotarea .* plot_max | |
png("/tmp/wut.png") #save the plot to png | |
cairo_img = read_from_png("/tmp/wut.png") #load in the png to cairo | |
pltwidth, pltheight = cairo_img.width, cairo_img.height | |
c = CairoRGBSurface( pltwidth, pltheight ); | |
cr = CairoContext( c ); | |
set_source_surface( cr, cairo_img, 0, 0 ); | |
gtkcvs = @GtkCanvas( pltwidth, pltheight ) | |
plot_extent_x = xplotlim[2] - xplotlim[1] | |
plot_extent_y = yplotlim[2] - yplotlim[1] | |
bndedpxl = ( (x .- xplotlim[1]) ./ plot_extent_x, | |
(y .- yplotlim[2]) ./ -plot_extent_y ) | |
widget_extent_x = vp_plot_area_px[2] - vp_plot_area_px[1] | |
widget_extent_y = vp_plot_area_px[4] - vp_plot_area_px[3] | |
#need to find upper Y limit of plot! | |
vp_plot_area_px[3] = (pltheight - vp_plot_area_px[4]) | |
npx = [ (bndedpxl[1] .* widget_extent_x) .+ vp_plot_area_px[1], | |
(bndedpxl[2] .* widget_extent_y) .+ vp_plot_area_px[3] ] # | |
return ClickablePlot( npx[1], npx[2], | |
gtkcvs, vp_plot_area_px, | |
cairo_img, xplotlim, yplotlim, | |
UInt8( 0 ), | |
Queue{ Tuple{ Float64,Float64 } }(), | |
Queue{ Tuple{ Float64,Float64 } }() | |
) | |
end | |
function render(ctx::CairoContext, cp::ClickablePlot) | |
set_source_surface(ctx, cp.cairoimg, 0, 0); | |
paint(ctx); fill(ctx) | |
return nothing | |
end | |
function render_line(ctx::CairoContext, start, finish ; dash = nothing, RGB = (0,0,0) ) | |
@assert(length(start) == 2); @assert(length(finish) == 2); @assert(length(RGB) == 3) | |
set_source_rgb( ctx, RGB... ); | |
if !isa( dash, Nothing ) | |
set_dash( ctx, dash ) | |
end | |
move_to( ctx, start... ); | |
line_to( ctx, finish... ); | |
Gtk.stroke(ctx) | |
return nothing | |
end | |
function attach_draw( cp::ClickablePlot ) | |
#Attach drawing routine | |
@guarded draw( cp.canvas ) do widget | |
ctx = getgc( cp.canvas ) | |
render(ctx, cp) | |
for line in cp.uicoords | |
render_line(ctx, [ line[1], 0 ], [ line[1], Gtk.height( cp.cairoimg )] ; | |
dash = [ 8.0, 8.0 ], RGB = ( 0, 0, 0 ) ) | |
end | |
end | |
end | |
#Attach mouse events | |
function attach_mouse_down( cp::ClickablePlot ) | |
cp.canvas.mouse.button1press = @guarded (widget, event) -> begin | |
#Check bounds to ensure we are clicking inside the plot | |
if ( cp.plotarea[ 1 ] < event.x < cp.plotarea[ 2 ] ) && | |
( cp.plotarea[ 3 ] < event.y < cp.plotarea[ 4 ] ) | |
ctx = getgc( cp.canvas ) | |
render(ctx, cp) | |
render_line(ctx, [ event.x, 0 ], [event.x, Gtk.height( cp.cairoimg )] ; | |
dash = [ 8.0, 8.0 ], RGB = ( 0, 0, 0 ) ) | |
reveal(widget) | |
#Event coords are pixel space | |
npx = [ (event.x - cp.plotarea[1]) / (cp.plotarea[2] - cp.plotarea[1]), | |
(event.y - cp.plotarea[3]) / (cp.plotarea[4] - cp.plotarea[3]) ] | |
plotspace = ( cp.xplotlim[1] + ( npx[1] * (cp.xplotlim[2] - cp.xplotlim[1]) ), | |
cp.yplotlim[2] - ( npx[2] * (cp.yplotlim[2] - cp.yplotlim[1]) ) ) | |
cp.uicoords = Queue{ Tuple{ Float64,Float64 } }() | |
cp.plotcoords = Queue{ Tuple{ Float64,Float64 } }() | |
enqueue!(cp.uicoords, ( event.x , event.y ) ) | |
enqueue!(cp.plotcoords, plotspace ) | |
#println( "\t Plot space: ", plotspace ) | |
cp.clickstate = 1 | |
reveal(widget) | |
else #reset clickstate someone clicked elsewhere | |
cp.clickstate = 0 | |
end | |
end | |
#handle dragging event | |
cp.canvas.mouse.button1motion = @guarded (widget, event) -> begin | |
#Check bounds to ensure we are clicking inside the plot | |
if ( cp.plotarea[ 1 ] < event.x < cp.plotarea[ 2 ] ) && | |
( cp.plotarea[ 3 ] < event.y < cp.plotarea[ 4 ] ) | |
if cp.clickstate == 1 | |
ctx = getgc( cp.canvas ) | |
render(ctx, cp) | |
for line in cp.uicoords | |
render_line(ctx, [ line[1], 0 ], [ line[1], Gtk.height( cp.cairoimg )] ; | |
dash = [ 8.0, 8.0 ], RGB = ( 0, 0, 0 ) ) | |
end | |
render_line(ctx, [ event.x, 0 ], [event.x, Gtk.height( cp.cairoimg )] ; | |
dash = [ 8.0, 8.0 ], RGB = ( 0.5, 0.5, 0.5 ) ) | |
reveal(widget) | |
end | |
end | |
end | |
end | |
function attach_mouse_up( cp::ClickablePlot ) | |
cp.canvas.mouse.button1release = @guarded (widget, event) -> begin | |
#Check bounds to ensure we are clicking inside the plot | |
if ( cp.plotarea[ 1 ] < event.x < cp.plotarea[ 2 ] ) && | |
( cp.plotarea[ 3 ] < event.y < cp.plotarea[ 4 ] ) | |
if cp.clickstate == 1 | |
#Event coords are pixel space | |
npx = [ (event.x - cp.plotarea[1]) / (cp.plotarea[2] - cp.plotarea[1]), | |
(event.y - cp.plotarea[3]) / (cp.plotarea[4] - cp.plotarea[3]) ] | |
plotspace = ( cp.xplotlim[1] + ( npx[1] * (cp.xplotlim[2] - cp.xplotlim[1]) ), | |
cp.yplotlim[2] - ( npx[2] * (cp.yplotlim[2] - cp.yplotlim[1]) ) ) | |
enqueue!(cp.uicoords, ( event.x , event.y ) ) | |
enqueue!(cp.plotcoords, plotspace ) | |
#println( "\t Plot space: ", cp.uicoords ) | |
ctx = getgc( cp.canvas ) | |
render(ctx, cp) | |
for line in cp.uicoords | |
render_line(ctx, [ line[1], 0 ], [ line[1], Gtk.height( cp.cairoimg )] ; | |
dash = [ 8.0, 8.0 ], RGB = ( 0, 0, 0 ) ) | |
end | |
mini, maxi = extrema( first.(cp.uicoords) ) | |
selection = findall( mini .< cp.X_ref .< maxi ) | |
for s in 2:length(selection) | |
move_to( ctx, cp.X_ref[selection[s]-1], cp.Y_ref[selection[s]-1] ); | |
line_to( ctx, cp.X_ref[selection[s]], cp.Y_ref[selection[s]] ); | |
Gtk.stroke(ctx) | |
end | |
reveal(widget) | |
end | |
else | |
cp.uicoords = Queue{ Tuple{ Float64,Float64 } }() | |
cp.plotcoords = Queue{ Tuple{ Float64,Float64 } }() | |
cp.clickstate = 0 | |
end | |
end | |
end | |
random_stuff = sin.( 0.0 : 0.01 : ( 10pi ) ) | |
rand_len = length(random_stuff) | |
random_stuff[500:1000] = random_stuff[500:1000] / 10 .- 3.0 | |
random_stuff[2360:end] = random_stuff[2360:end] .* exp.((2360:rand_len) ./ rand_len) | |
a = Plots.plot(random_stuff, fmt = :png, title = "", legend = false) | |
click_plot = ClickablePlot( a ) | |
attach_draw( click_plot ) | |
attach_mouse_down( click_plot ) | |
attach_mouse_up( click_plot ) | |
g = GtkGrid() | |
g[ 1, 1 ] = GtkLabel( "Click and Drag on the Plot to select a region:" ) | |
g[ 1, 2 ] = click_plot.canvas | |
w = GtkWindow( g, "Plot Viewer", 700, 500 ); | |
Gtk.showall( w ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment