|
1 | 1 |
|
| 2 | +function gui(outbase, files; colors=distinguishable_colors(15, [RGB(1, 1, 1)]; dropseed=true)) |
| 3 | + channelpct(x) = string(round(Int, x * 100)) * '%' |
2 | 4 |
|
| 5 | + outbase, _ = splitext(outbase) |
| 6 | + |
| 7 | + winsize = round.(Int, 0.8 .* screen_size()) |
| 8 | + win = GtkWindow("CounterMarking", winsize...) |
| 9 | + ag = Gtk4.GLib.GSimpleActionGroup() |
| 10 | + m = Gtk4.GLib.GActionMap(ag) |
| 11 | + push!(win, Gtk4.GLib.GActionGroup(ag), "win") |
| 12 | + Gtk4.GLib.add_action(m, "close", ImageView.close_cb, win) |
| 13 | + Gtk4.GLib.add_action(m, "closeall", ImageView.closeall_cb, nothing) |
| 14 | + Gtk4.GLib.add_stateful_action(m, "fullscreen", false, ImageView.fullscreen_cb, win) |
| 15 | + sc = GtkShortcutController(win) |
| 16 | + Gtk4.add_action_shortcut(sc,Sys.isapple() ? "<Meta>W" : "<Control>W", "win.close") |
| 17 | + Gtk4.add_action_shortcut(sc,Sys.isapple() ? "<Meta><Shift>W" : "<Control><Shift>W", "win.closeall") |
| 18 | + Gtk4.add_action_shortcut(sc,Sys.isapple() ? "<Meta><Shift>F" : "F11", "win.fullscreen") |
| 19 | + |
| 20 | + # CSS styling for the colors |
| 21 | + io = IOBuffer() |
| 22 | + for (i, color) in enumerate(colors) |
| 23 | + colorstr = "rgb(" * channelpct(color.r) * ", " * |
| 24 | + channelpct(color.g) * ", " * |
| 25 | + channelpct(color.b) * ")" |
| 26 | + println(io, """ |
| 27 | + .color$i { |
| 28 | + background: $colorstr; |
| 29 | + } |
| 30 | + """) |
| 31 | + end |
| 32 | + css = String(take!(io)) |
| 33 | + cssprov = GtkCssProvider(css) |
| 34 | + push!(Gtk4.display(win), cssprov) |
| 35 | + |
| 36 | + win[] = bx = GtkBox(:v) |
| 37 | + ImageView.window_wrefs[win] = nothing |
| 38 | + signal_connect(win, :destroy) do w |
| 39 | + delete!(ImageView.window_wrefs, win) |
| 40 | + end |
| 41 | + g, frames, canvases = ImageView.canvasgrid((2, 1), :auto) |
| 42 | + push!(bx, g) |
| 43 | + push!(bx, GtkSeparator(:h)) |
| 44 | + guibx = GtkBox(:h) |
| 45 | + push!(bx, guibx) |
| 46 | + seggrid = GtkGrid() |
| 47 | + push!(guibx, seggrid) |
| 48 | + # Add checkboxes for each color, with the box's color set to the color |
| 49 | + cbs = [] |
| 50 | + for i in 1:length(colors) |
| 51 | + cb = checkbox(false) |
| 52 | + add_css_class(cb.widget, "color$i") |
| 53 | + for prop in ("margin_start", "margin_end", "margin_top", "margin_bottom") |
| 54 | + set_gtk_property!(cb.widget, prop, 5) |
| 55 | + end |
| 56 | + set_gtk_property!(cb.widget, "width-request", 20) |
| 57 | + row = div(i - 1, 5) + 1 |
| 58 | + col = mod(i - 1, 5) + 1 |
| 59 | + seggrid[col, row] = cb.widget |
| 60 | + push!(cbs, cb) |
| 61 | + end |
| 62 | + # Add "Done & Next" and "Skip" buttons |
| 63 | + btnclick = Condition() |
| 64 | + whichbutton = Ref{Symbol}() |
| 65 | + donebtn = button("Done & Next") |
| 66 | + skipbtn = button("Skip") |
| 67 | + push!(guibx, donebtn) |
| 68 | + push!(guibx, skipbtn) |
| 69 | + on(donebtn) do _ |
| 70 | + whichbutton[] = :done |
| 71 | + notify(btnclick) |
| 72 | + end |
| 73 | + on(skipbtn) do _ |
| 74 | + whichbutton[] = :skip |
| 75 | + notify(btnclick) |
| 76 | + end |
| 77 | + |
| 78 | + results = [] |
| 79 | + for (i, file) in enumerate(files) |
| 80 | + img = color.(load(file)) |
| 81 | + seg = segment_image(img) |
| 82 | + nsegs = length(segment_labels(seg)) |
| 83 | + @assert nsegs < length(colors) "Too many segments for colors" |
| 84 | + istim = stimulus_index(seg) |
| 85 | + for (j, cb) in enumerate(cbs) |
| 86 | + # set_gtk_property!(cb, "active", j <= nsegs) |
| 87 | + cb[] = j == istim |
| 88 | + end |
| 89 | + imshow(canvases[1, 1], img) |
| 90 | + imshow(canvases[2, 1], map(i->colors[i], labels_map(seg))) |
| 91 | + |
| 92 | + wait(btnclick) |
| 93 | + whichbutton[] == :skip && continue |
| 94 | + |
| 95 | + keep = Int[] |
| 96 | + for (j, cb) in enumerate(cbs) |
| 97 | + if cb[] |
| 98 | + push!(keep, j) |
| 99 | + end |
| 100 | + end |
| 101 | + pixelskeep = map(i -> i ∈ keep, labels_map(seg)) |
| 102 | + L = label_components(pixelskeep) |
| 103 | + newseg = SegmentedImage(img, L) |
| 104 | + spotdict, stimulus = spots(newseg) |
| 105 | + push!(results, (file, spotdict, stimulus, newseg)) |
| 106 | + end |
| 107 | + |
| 108 | + if !isempty(results) |
| 109 | + xlsxname = outbase * ".xlsx" |
| 110 | + XLSX.openxlsx(xlsxname; mode="w") do xf |
| 111 | + for (i, (file, spotdict, stimulus, seg)) in enumerate(results) |
| 112 | + imgsize = size(labels_map(seg)) |
| 113 | + sheetname = splitext(basename(file))[1] |
| 114 | + sheet = if i == 1 |
| 115 | + XLSX.rename!(xf[1], sheetname) |
| 116 | + xf[1] |
| 117 | + else |
| 118 | + XLSX.addsheet!(xf, sheetname) |
| 119 | + end |
| 120 | + makesheet!(sheet, spotdict, stimulus, imgsize) |
| 121 | + end |
| 122 | + end |
| 123 | + jldname = outbase * ".jld2" |
| 124 | + jldopen(jldname, "w") do jf |
| 125 | + for (file, spotdict, stimulus, seg) in results |
| 126 | + imgname = splitext(basename(file))[1] |
| 127 | + write(jf, imgname, (labels_map(seg), spotdict, stimulus)) |
| 128 | + end |
| 129 | + end |
| 130 | + end |
| 131 | + destroy(win) |
| 132 | +end |
| 133 | +gui(outbase, glob::Glob.GlobMatch; kwargs...) = gui(outbase, Glob.glob(glob); kwargs...) |
3 | 134 |
|
4 | 135 | function linkpair(img, imgc) |
5 | 136 | zr, slicedata = roi(img) |
|
0 commit comments