Skip to content

Instantly share code, notes, and snippets.

@sairus7
Last active December 2, 2021 13:58
Show Gist options
  • Save sairus7/19778c05ffba7c677a3414f752a0f9cb to your computer and use it in GitHub Desktop.
Save sairus7/19778c05ffba7c677a3414f752a0f9cb to your computer and use it in GitHub Desktop.
ImPlot example with state-management and less boilerplate
include("Renderer.jl")
using .Renderer
using CImGui
using CImGui.CSyntax
using CImGui.CSyntax.CStatic
import CImGui.LibCImGui: ImGuiCond_Always, ImGuiCond_Once
using ImPlot
using UnPack
# GUI state
Base.@kwdef mutable struct MyStates
show_another_window = false
# make up some data
xs1 = Float64.(collect(1:100))
ys1 = rand(6000)
noise = rand(length(xs1))
bar_vals = Vector{Vector{Float64}}(undef, 5)
bar_maxes = zeros(5)
bar_mins = zeros(5)
bar_max = 0.0
bar_min = 0.0
bar_counter = 1
shaded_x = 1:1000
shaded_y1 = [sin(x) for x in range(0,2π, length = length(shaded_x))]
shaded_y_ref = -2.0
end
# something that cannot be brought to MyStates constructor
function init(state::MyStates)
@unpack bar_vals, bar_maxes, bar_mins, shaded_x, shaded_y1 = state
for i in 1:length(bar_vals)
bar_vals[i] = Float64.(collect(range(rand(1:10), step=rand([-2,-1,1,2]), length=120)))
bar_maxes[i] = maximum(bar_vals[i])
bar_mins[i] = minimum(bar_vals[i])
end
state.bar_max = maximum(bar_maxes)
state.bar_min = minimum(bar_mins)
end
function update(state::MyStates)
@unpack show_another_window, xs1, ys1, noise, bar_vals, bar_maxes, bar_mins, bar_max, bar_min, bar_counter,
shaded_x, shaded_y1, shaded_y_ref = state
ys1 .= rand(6000)
noise .= xs1 .+ rand(-5.0:0.1:5.0, length(xs1))
if bar_counter == 120
state.bar_counter = 1
else
state.bar_counter += 1
end
end
# this is the UI function, whenever the structure of `MyStates` is changed,
function ui(state::MyStates)
@unpack show_another_window, xs1, ys1, noise, bar_vals, bar_maxes, bar_mins, bar_max, bar_min, bar_counter,
shaded_x, shaded_y1, shaded_y_ref = state
CImGui.Begin("Example Plots")
@c CImGui.Checkbox("Show Examples", &state.show_another_window)
CImGui.Text("Application average $(1000 / CImGui.GetIO().Framerate) ms/frame ($(CImGui.GetIO().Framerate) FPS)")
CImGui.End()
if state.show_another_window
@c CImGui.Begin("Examples Window", &state.show_another_window)
if CImGui.CollapsingHeader("Line plots")
ImPlot.SetNextPlotLimits(0.0, 6000, 0.0, 1.0, ImGuiCond_Always)
# Using '##' in the label name hides the plot label, but lets
# us keep the label ID unique for modifying styling etc.
if ImPlot.BeginPlot("##line", "x1", "y1", CImGui.ImVec2(-1,300))
ImPlot.PlotLine(ys1)
ImPlot.EndPlot()
end
end
if CImGui.CollapsingHeader("Scatter plot")
ImPlot.SetNextPlotLimits(0,100,-5,105, ImGuiCond_Always)
if ImPlot.BeginPlot("##scatter", "x2", "y2", CImGui.ImVec2(-1,300))
ImPlot.PlotScatter(xs1, noise)
ImPlot.EndPlot()
end
end
if CImGui.CollapsingHeader("Bar plot")
bar_val_step = [bar_vals[j][bar_counter] for j in 1:length(bar_vals)]
ImPlot.SetNextPlotLimits(-0.5, 4.5, bar_min, bar_max, ImGuiCond_Always)
if ImPlot.BeginPlot("##bars", "", "", CImGui.ImVec2(-1,300))
ImPlot.PlotBars(bar_val_step)
ImPlot.EndPlot()
end
end
if CImGui.CollapsingHeader("Shaded plot")
ImPlot.SetNextPlotLimits(0,1000,-2,1, ImGuiCond_Always)
if ImPlot.BeginPlot("##shaded", "", "", CImGui.ImVec2(-1,300))
ImPlot.PlotShaded(shaded_x, shaded_y1, shaded_y_ref)
ImPlot.EndPlot()
end
end
CImGui.End()
end
end
state = MyStates()
init(state)
update(state) # initial update
Renderer.render(()->ui(state), width = 360, height = 480, title = "A simple UI")
function infinite_loop(state::MyStates)
@async while true
update(state)
yield()
end
end
infinite_loop(state) # animate the plot
module Renderer
using CImGui
using CImGui.GLFWBackend
using CImGui.OpenGLBackend
using CImGui.GLFWBackend.GLFW
using CImGui.OpenGLBackend.ModernGL
function __init__()
@static if Sys.isapple()
# OpenGL 3.2 + GLSL 150
global glsl_version = 150
GLFW.WindowHint(GLFW.CONTEXT_VERSION_MAJOR, 3)
GLFW.WindowHint(GLFW.CONTEXT_VERSION_MINOR, 2)
GLFW.WindowHint(GLFW.OPENGL_PROFILE, GLFW.OPENGL_CORE_PROFILE) # 3.2+ only
GLFW.WindowHint(GLFW.OPENGL_FORWARD_COMPAT, GL_TRUE) # required on Mac
else
# OpenGL 3.0 + GLSL 130
global glsl_version = 130
GLFW.WindowHint(GLFW.CONTEXT_VERSION_MAJOR, 3)
GLFW.WindowHint(GLFW.CONTEXT_VERSION_MINOR, 0)
# GLFW.WindowHint(GLFW.OPENGL_PROFILE, GLFW.OPENGL_CORE_PROFILE) # 3.2+ only
# GLFW.WindowHint(GLFW.OPENGL_FORWARD_COMPAT, GL_TRUE) # 3.0+ only
end
end
error_callback(err::GLFW.GLFWError) = @error "GLFW ERROR: code $(err.code) msg: $(err.description)"
function init_renderer(width, height, title::AbstractString)
# setup GLFW error callback
GLFW.SetErrorCallback(error_callback)
# create window
window = GLFW.CreateWindow(width, height, title)
@assert window != C_NULL
GLFW.MakeContextCurrent(window)
GLFW.SwapInterval(1) # enable vsync
# setup Dear ImGui context
ctx = CImGui.CreateContext()
# setup Dear ImGui style
CImGui.StyleColorsDark()
# CImGui.StyleColorsClassic()
# CImGui.StyleColorsLight()
# setup Platform/Renderer bindings
ImGui_ImplGlfw_InitForOpenGL(window, true)
ImGui_ImplOpenGL3_Init(glsl_version)
return window, ctx
end
function renderloop(window, ctx, ui=()->nothing, hotloading=false)
try
while !GLFW.WindowShouldClose(window)
GLFW.PollEvents()
ImGui_ImplOpenGL3_NewFrame()
ImGui_ImplGlfw_NewFrame()
CImGui.NewFrame()
hotloading ? Base.invokelatest(ui) : ui()
CImGui.Render()
GLFW.MakeContextCurrent(window)
display_w, display_h = GLFW.GetFramebufferSize(window)
glViewport(0, 0, display_w, display_h)
glClearColor(0.2, 0.2, 0.2, 1)
glClear(GL_COLOR_BUFFER_BIT)
ImGui_ImplOpenGL3_RenderDrawData(CImGui.GetDrawData())
GLFW.MakeContextCurrent(window)
GLFW.SwapBuffers(window)
yield()
end
catch e
@error "Error in renderloop!" exception=e
Base.show_backtrace(stderr, catch_backtrace())
finally
ImGui_ImplOpenGL3_Shutdown()
ImGui_ImplGlfw_Shutdown()
CImGui.DestroyContext(ctx)
GLFW.DestroyWindow(window)
end
end
function render(ui; width=1280, height=720, title::AbstractString="Demo", hotloading=false)
window, ctx = init_renderer(width, height, title)
GC.@preserve window ctx begin
t = @async renderloop(window, ctx, ui, hotloading)
end
return t
end
end # module
@zsoerenm
Copy link

zsoerenm commented Dec 2, 2021

If I run this app and click to open one of the CollapsingHeader, I will get the following error:

signal (11): Speicherzugriffsfehler
in expression starting at none:0
_ZN6ImPlot18SetNextPlotLimitsXEddi at /home/zorn/.julia/artifacts/5b2b61e1b0721d0f69fa855d20f464a98f4df73f/lib/libcimplot.so (unknown line)
_ZN6ImPlot17SetNextPlotLimitsEddddi at /home/zorn/.julia/artifacts/5b2b61e1b0721d0f69fa855d20f464a98f4df73f/lib/libcimplot.so (unknown line)
SetNextPlotLimits at /home/zorn/.julia/packages/ImPlot/kI3XA/src/libcimplot.jl:1238 [inlined]
ui at /home/zorn/Dokumente/Projekte/Dissertation/Analyse/src/testing.jl:72
#12 at /home/zorn/Dokumente/Projekte/Dissertation/Analyse/src/testing.jl:114
renderloop at /home/zorn/.julia/packages/CImGui/svEI7/examples/Renderer.jl:62
#4 at ./task.jl:423
unknown function (ip: 0x7fde96721d4f)
_jl_invoke at /buildworker/worker/package_linux64/build/src/gf.c:2247 [inlined]
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2429
jl_apply at /buildworker/worker/package_linux64/build/src/julia.h:1788 [inlined]
start_task at /buildworker/worker/package_linux64/build/src/task.c:877
Allocations: 31449336 (Pool: 31433784; Big: 15552); GC: 35
zsh: segmentation fault (core dumped)  julia

EDIT: BTW I use Julia v1.7

@sairus7
Copy link
Author

sairus7 commented Dec 2, 2021

You can try Renderer from CImGui master branch: https://github.com/Gnimuc/CImGui.jl/blob/master/examples/Renderer.jl

@zsoerenm
Copy link

zsoerenm commented Dec 2, 2021

Thanks for the suggestion. I tried the example Renderer. However, there is now a different error:
With the latest tagged version of CImGui (v1.79.0) I get:

ERROR: LoadError: InitError: UndefVarError: glfwWindowHint not defined

Then I tried to update to latest master (v1.82.0) (with add CImGui#master) and there I get:

ERROR: LoadError: UndefVarError: GLFWBackend not defined

@zsoerenm
Copy link

zsoerenm commented Dec 2, 2021

I figured, that I have to replace

using CImGui.GLFWBackend
using CImGui.OpenGLBackend
using CImGui.GLFWBackend.GLFW
using CImGui.OpenGLBackend.ModernGL

with

using CImGui.ImGuiGLFWBackend
using CImGui.ImGuiOpenGLBackend
using CImGui.ImGuiGLFWBackend.LibGLFW
using CImGui.ImGuiOpenGLBackend.ModernGL

But then I run into the next error:

ERROR: LoadError: UndefVarError: ImGui_ImplGlfw_InitForOpenGL not defined

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment