GLMakie Menu() maximum number of items?

Is there a maximum number of items that can be added to a Menu? I’m experimenting with GLMakie and Observables. My program watches a folder for new files. Both the data and Menu options are Observables, so when a new file appears it is plotted and a new item is added to the Menu options so a user can select this and all previous data. My code works, but crashes whenever I add a 7th item. I’m having difficulty parsing the error message, but my first guess is that there is some upper limit to the number of items a Menu can have based on the GLMakie screen size or something?

julia> GUIMakie.run("./data/")
New file: notworking.lvm
New file: sig_220607_121211.lvm
New file: sig_220607_154155.lvm
New file: sig_220607_174053.lvm
New file: sig_220607_174239.lvm
New file: three_stacks.lvm
New file: two_stacks_noxdata.lvm
ERROR: InexactError: trunc(UInt8, 256)
Stacktrace:
  [1] throw_inexacterror(f::Symbol, #unused#::Type{UInt8}, val::Int64)
    @ Core ./boot.jl:612
  [2] checked_trunc_uint
    @ ./boot.jl:642 [inlined]
  [3] toUInt8
    @ ./boot.jl:704 [inlined]
  [4] UInt8
    @ ./boot.jl:764 [inlined]
  [5] convert
    @ ./number.jl:7 [inlined]
  [6] cvt1
    @ ./essentials.jl:343 [inlined]
  [7] ntuple
    @ ./ntuple.jl:49 [inlined]
  [8] convert
    @ ./essentials.jl:344 [inlined]
  [9] push!
    @ ./array.jl:994 [inlined]
 [10] #117
    @ ~/.julia/packages/GLMakie/SJcSR/src/screen.jl:259 [inlined]
 [11] get!(default::GLMakie.var"#117#119"{GLMakie.Screen, Makie.Scene}, h::Dict{WeakRef, UInt8}, key::WeakRef)
    @ Base ./dict.jl:464
 [12] push!(screen::GLMakie.Screen, scene::Makie.Scene, robj::GLMakie.GLAbstraction.RenderObject{GLMakie.GLAbstraction.StandardPrerender})
    @ GLMakie ~/.julia/packages/GLMakie/SJcSR/src/screen.jl:257
 [13] cached_robj!(robj_func::GLMakie.var"#245#246"{Makie.Scene, MakieCore.Mesh{Tuple{GeometryBasics.Mesh{2, Float32, GeometryBasics.Ngon{2, Float32, 3, GeometryBasics.Point{2, Float32}}, GeometryBasics.SimpleFaceView{2, Float32, 3, GeometryBasics.OffsetInteger{-1, UInt32}, GeometryBasics.Point{2, Float32}, GeometryBasics.NgonFace{3, GeometryBasics.OffsetInteger{-1, UInt32}}}}}}}, screen::GLMakie.Screen, scene::Makie.Scene, x::MakieCore.Mesh{Tuple{GeometryBasics.Mesh{2, Float32, GeometryBasics.Ngon{2, Float32, 3, GeometryBasics.Point{2, Float32}}, GeometryBasics.SimpleFaceView{2, Float32, 3, GeometryBasics.OffsetInteger{-1, UInt32}, GeometryBasics.Point{2, Float32}, GeometryBasics.NgonFace{3, GeometryBasics.OffsetInteger{-1, UInt32}}}}}})
    @ GLMakie ~/.julia/packages/GLMakie/SJcSR/src/drawing_primitives.jl:113
 [14] draw_atomic
    @ ~/.julia/packages/GLMakie/SJcSR/src/drawing_primitives.jl:493 [inlined]
 [15] insert!
    @ ~/.julia/packages/GLMakie/SJcSR/src/drawing_primitives.jl:121 [inlined]
 [16] (::GLMakie.var"#177#178"{GLMakie.Screen, Makie.Scene})(x::MakieCore.Mesh{Tuple{GeometryBasics.Mesh{2, Float32, GeometryBasics.Ngon{2, Float32, 3, GeometryBasics.Point{2, Float32}}, GeometryBasics.SimpleFaceView{2, Float32, 3, GeometryBasics.OffsetInteger{-1, UInt32}, GeometryBasics.Point{2, Float32}, GeometryBasics.NgonFace{3, GeometryBasics.OffsetInteger{-1, UInt32}}}}}})
    @ GLMakie ~/.julia/packages/GLMakie/SJcSR/src/drawing_primitives.jl:126
 [17] foreach(f::GLMakie.var"#177#178"{GLMakie.Screen, Makie.Scene}, itr::Vector{MakieCore.AbstractPlot})
    @ Base ./abstractarray.jl:2712
 [18] insert!
    @ ~/.julia/packages/GLMakie/SJcSR/src/drawing_primitives.jl:123 [inlined]
 [19] (::GLMakie.var"#177#178"{GLMakie.Screen, Makie.Scene})(x::MakieCore.Mesh{Tuple{Vector{GeometryBasics.Mesh{2, Float32, GeometryBasics.Ngon{2, Float32, 3, GeometryBasics.Point{2, Float32}}, GeometryBasics.SimpleFaceView{2, Float32, 3, GeometryBasics.OffsetInteger{-1, UInt32}, GeometryBasics.Point{2, Float32}, GeometryBasics.NgonFace{3, GeometryBasics.OffsetInteger{-1, UInt32}}}}}}})
    @ GLMakie ~/.julia/packages/GLMakie/SJcSR/src/drawing_primitives.jl:126
 [20] foreach(f::GLMakie.var"#177#178"{GLMakie.Screen, Makie.Scene}, itr::Vector{MakieCore.AbstractPlot})
    @ Base ./abstractarray.jl:2712
 [21] insert!(screen::GLMakie.Screen, scene::Makie.Scene, x::MakieCore.Combined)
    @ GLMakie ~/.julia/packages/GLMakie/SJcSR/src/drawing_primitives.jl:123
 [22] push!(scene::Makie.Scene, plot::MakieCore.Combined{Makie.poly, Tuple{Vector{Vector{GeometryBasics.Point{2, Float32}}}}})
    @ Makie ~/.julia/packages/Makie/Ws9gB/src/scenes.jl:409
 [23] plot!(scene::Makie.Scene, P::Type{MakieCore.Combined{Makie.poly, Tuple{GeometryBasics.HyperRectangle{2, Int64}}}}, attributes::MakieCore.Attributes, input::Tuple{Observables.Observable{GeometryBasics.HyperRectangle{2, Int64}}}, args::Observables.Observable{Tuple{Vector{Vector{GeometryBasics.Point{2, Float32}}}}})
    @ Makie ~/.julia/packages/Makie/Ws9gB/src/interfaces.jl:403
 [24] plot!(scene::Makie.Scene, P::Type{MakieCore.Combined{Makie.poly}}, attributes::MakieCore.Attributes, args::Observables.Observable{GeometryBasics.HyperRectangle{2, Int64}}; kw_attributes::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ Makie ~/.julia/packages/Makie/Ws9gB/src/interfaces.jl:320
 [25] plot!
    @ ~/.julia/packages/Makie/Ws9gB/src/interfaces.jl:288 [inlined]
 [26] #plot!#132
    @ ~/.julia/packages/Makie/Ws9gB/src/interfaces.jl:271 [inlined]
 [27] poly!(::Makie.Scene, ::Vararg{Any}; attributes::Base.Pairs{Symbol, Any, NTuple{5, Symbol}, NamedTuple{(:color, :visible, :strokecolor, :strokewidth, :inspectable), Tuple{Observables.Observable{Any}, Observables.Observable{Any}, Observables.Observable{ColorTypes.RGB{Float32}}, Observables.Observable{Any}, Bool}}})
    @ Makie ~/.julia/packages/MakieCore/C4HO7/src/recipes.jl:37
 [28] initialize_block!(box::Makie.Box)
    @ Makie ~/.julia/packages/Makie/Ws9gB/src/makielayout/blocks/box.jl:11
 [29] _block(::Type{Makie.Box}, ::Makie.Scene; bbox::Nothing, kwargs::Base.Pairs{Symbol, Any, NTuple{5, Symbol}, NamedTuple{(:width, :height, :color, :strokewidth, :visible), Tuple{Nothing, Nothing, ColorTypes.RGB{Float32}, Int64, Observables.Observable{Any}}}})
    @ Makie ~/.julia/packages/Makie/Ws9gB/src/makielayout/blocks.jl:393
 [30] #_#996
    @ ~/.julia/packages/Makie/Ws9gB/src/makielayout/blocks.jl:269 [inlined]
 [31] _reassemble_menu(m::Makie.Menu, scene::Makie.Scene, selectionrect::Makie.Box, selectiontext::Makie.Label, allrects::Vector{Makie.Box}, alltexts::Vector{Makie.Label}, mouseeventhandles::Vector{Makie.MouseEventHandle}, contentgrid::GridLayoutBase.GridLayout, optionstrings::Base.RefValue{Vector{String}})
    @ Makie ~/.julia/packages/Makie/Ws9gB/src/makielayout/blocks/menu.jl:251
 [32] (::Makie.var"#1667#1679"{Makie.Menu, Vector{Makie.MouseEventHandle}, Vector{Makie.Label}, Vector{Makie.Box}, Makie.Label, Observables.Observable{String}, Base.RefValue{Vector{String}}, Makie.Box, GridLayoutBase.GridLayout, Makie.Scene})(options::Vector{String})
    @ Makie ~/.julia/packages/Makie/Ws9gB/src/makielayout/blocks/menu.jl:116
 [33] #invokelatest#2
    @ ./essentials.jl:716 [inlined]
 [34] invokelatest
    @ ./essentials.jl:714 [inlined]
 [35] notify
    @ ~/.julia/packages/Observables/ynr7h/src/Observables.jl:143 [inlined]
 [36] setindex!(observable::Observables.Observable, val::Any)
    @ Observables ~/.julia/packages/Observables/ynr7h/src/Observables.jl:86
 [37] #15
    @ ~/.julia/packages/Observables/ynr7h/src/Observables.jl:393 [inlined]
 [38] (::Observables.var"#callback#13"{Observables.var"#15#16"{Makie.var"#1009#1010"{DataType}, Observables.Observable{Any}}, Tuple{Observables.Observable{Vector{String}}}})(x::Any)
    @ Observables ~/.julia/packages/Observables/ynr7h/src/Observables.jl:339
 [39] #invokelatest#2
    @ ./essentials.jl:716 [inlined]
 [40] invokelatest
    @ ./essentials.jl:714 [inlined]
 [41] notify
    @ ~/.julia/packages/Observables/ynr7h/src/Observables.jl:143 [inlined]
 [42] setindex!(observable::Observables.Observable, val::Any)
    @ Observables ~/.julia/packages/Observables/ynr7h/src/Observables.jl:86
 [43] run(datadir::String, extension::String)
    @ GUIMakie ~/Documents/projects/GUIMakie/src/gui.jl:90
 [44] run(datadir::String)
    @ GUIMakie ~/Documents/projects/GUIMakie/src/gui.jl:32
 [45] top-level scope
    @ REPL[3]:1

1 Like

In the last refactor each Block got its own container scene and Menu uses Labels and Boxes internally which is now rather inefficient. That’s why you hit the 255 relatively quickly. I need to rewrite menu so it uses only one poly and one text object I think

1 Like

I see, that makes sense. Also explains why it gets slow very quickly. For the record, I made a version of this with only one Menu and I’m able to add many more plots (I’ve tested with 14 so far) without it crashing. But Menu gets very slow when switching between plots with this many.

I think what I’ll do for now is decide a limit and boot the oldest plots from the Menu.

(Second example with a live-updating plot (right) and a plot connected to the menu (left)

Yes the code for menu was written rather quickly as a proof of concept at the time and didn’t pay much attention to performance, so an overhaul is much needed

2 Likes

I should have checked the Issues. There is one from just a few weeks ago on this very topic: Issue #2013

Can you try with Makie 0.17.7 that is being released right this second? It should work much better for large menus.

Yeah, the update significantly speeds up menus. I tried both the two-panel figure and four-panel figure again. I added 10 plots and the “live” plot updates faster, so I guess the lag between adding a file and updating the plot was because Menu was taking a while as it got larger. Switching between plots is much faster now even at 10 plots in the menu. The four-panel figure no longer crashes.

I notice two slight problem areas:

  1. Sometimes the menu doesn’t register a click. I’m not sure if that’s because there’s a time lag after a selection is made before another selection can be made or it’s just not registering some clicks. The non-registering clicks occur both for opening the menu and making a selection and happens even for a small menu (3 items).

  2. When an open menu covers a plot, clicks will sometimes manipulate the plot instead of registering as a menu click.

This behavior is pretty inconsistent. Sometimes menu is smooth and registers almost all clicks correctly. Sometimes it doesn’t register several clicks in a row or two or three clicks will register on the plot behind menu. Here’s an animation:

Menu demo in Makie  0.17.7

I also notice a new bug with DataInspector. Did something change with how that should be initiated? I tried a minimal example and it still errors.

fig = Figure
ax = Axis([fig[1, 1])
lines!(ax, rand(10), rand(10))
inspector = DataInspector(fig)

Error message:

**ERROR:** type Array has no field extents
Stacktrace:
[1] **getproperty**
@ ./Base.jl:42 [inlined]
[2] **Bbox_from_glyphcollection(**text::String, gc::Vector{Point{2, Float32}}**)**
@ Makie ~/.julia/packages/Makie/fJ4OE/src/interaction/inspector.jl:191
[3] **(::Makie.var"#883#886"{Observable{Any}})(**gc::Vector{Point{2, Float32}}, pos::Point{2, Float32}, pad::Vec{4, Float32}**)**
@ Makie ~/.julia/packages/Makie/fJ4OE/src/interaction/inspector.jl:294
[4] **#map#19**
@ ~/.julia/packages/Observables/ynr7h/src/Observables.jl:444 [inlined]
[5] **map(**::Makie.var"#883#886"{Observable{Any}}, ::Observable{Vector{Point{2, Float32}}}, ::Observable{Any}, ::Observable{Any}**)**
@ Observables ~/.julia/packages/Observables/ynr7h/src/Observables.jl:444
[6] **plot!(**plot::Combined{Makie._inspector, Tuple{Int64}}**)**
@ Makie ~/.julia/packages/Makie/fJ4OE/src/interaction/inspector.jl:293
[7] **plot!(**scene::Scene, P::Type{Combined{Makie._inspector, Tuple{Int64}}}, attributes::Attributes, input::Tuple{Observable{Int64}}, args::Observable{Tuple{Int64}}**)**
@ Makie ~/.julia/packages/Makie/fJ4OE/src/interfaces.jl:402
[8] **plot!(**scene::Scene, P::Type{Combined{Makie._inspector}}, attributes::Attributes, args::Int64; kw_attributes::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}**)**
@ Makie ~/.julia/packages/Makie/fJ4OE/src/interfaces.jl:320
[9] **plot!**
@ ~/.julia/packages/Makie/fJ4OE/src/interfaces.jl:288 [inlined]
[10] **#plot!#132**
@ ~/.julia/packages/Makie/fJ4OE/src/interfaces.jl:271 [inlined]
[11] **_inspector!(**::Scene, ::Vararg{Any}; attributes::Base.Pairs{Symbol, Observable{StaticArrays.SMatrix{4, 4, Float32, 16}}, Tuple{Symbol}, NamedTuple{(:_root_px_projection,), Tuple{Observable{StaticArrays.SMatrix{4, 4, Float32, 16}}}}}**)**
@ Makie ~/.julia/packages/MakieCore/4Hq26/src/recipes.jl:37
[12] **DataInspector(**scene::Scene; priority::Int64, kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}**)**
@ Makie ~/.julia/packages/Makie/fJ4OE/src/interaction/inspector.jl:442
[13] **DataInspector**
@ ~/.julia/packages/Makie/fJ4OE/src/interaction/inspector.jl:439 [inlined]
[14] **#DataInspector#889**
@ ~/.julia/packages/Makie/fJ4OE/src/interaction/inspector.jl:435 [inlined]
[15] **DataInspector(**fig_or_block::Figure**)**
@ Makie ~/.julia/packages/Makie/fJ4OE/src/interaction/inspector.jl:435
[16] top-level scope
@ REPL[11]:1