Gnimuc / CImGui.jl

Julia wrapper for cimgui
https://github.com/cimgui/cimgui
MIT License
253 stars 25 forks source link

Input widgets with different IDs conflict with each other? #78

Closed FaresX closed 1 month ago

FaresX commented 1 year ago
屏幕截图_20230107_131500

As the picture shows, when I input 1 in the first InputText Widget, any other Input Widgets will be filled with the same contents, though I have used different IDs. Here is some of the codes:

function edit(insbuf::InstrBuffer, addr)
    for qt in insbuf.quantities
        edit(qt, insbuf.instrnm, addr)
    end
end

function edit(qt::InstrQuantity, instrnm, addr, ::Val{:set})
    id = string(instrnm, "-", addr, "-", qt.name)
    ftsz = CImGui.GetFontSize()
    CImGui.Text(qt.alias)
    CImGui.SameLine(ftsz*(maxaliaslist[instrnm]/3+0.5))
    CImGui.Text(":"); CImGui.SameLine() ###alias
    Us = conf["U"][qt.utype]
    U = isempty(Us) ? "" : Us[qt.uindex]
    CImGui.PushStyleVar(CImGui.ImGuiStyleVar_ItemSpacing, (0, 2))
    width = (CImGui.GetContentRegionAvailWidth()-6ftsz)/2
    CImGui.PushItemWidth(width)
    CImGui.InputTextWithHint("##设置$id", "设置值", qt.set, length(qt.set))
    CImGui.PopItemWidth()
    CImGui.SameLine() ###设置值
    valstr = qt.read
    val = U == "" ? valstr : @trypass string(parse(Float64, valstr)/ustrip(upreferred(U), 1U)) valstr
    CImGui.PushFont(secondft)
    CImGui.Button(string(val, "##$id"), (width, Float32(0)))
    CImGui.PopFont()
    CImGui.SameLine() ###实际值
    CImGui.PushItemWidth(3ftsz)
    @c showunit("##insbuf$id", qt.utype, &qt.uindex)
    CImGui.PopItemWidth()
    CImGui.PopStyleVar()
    CImGui.SameLine() ###单位
    CImGui.PushStyleVar(CImGui.ImGuiStyleVar_FrameRounding, 6)
    if CImGui.Button(" 确认 ##$id")
        if addr != ""
            svstr = replace(qt.set, r"\0.*"=>"")
            sv = U == "" ? svstr : @trypass string(eval(Meta.parse(sv)))*ustrip(upreferred(U), 1U) svstr
            instr = INSTR(instrnm, addr)
            setfunc = Symbol(instrnm, :_, qt.name, :_set)
            getfunc = Symbol(instrnm, :_, qt.name, :_get)
            lockstates() do
                @trylink_do instr (eval(:($setfunc($instr, $sv))); qt.read = eval(:($getfunc($instr)))) nothing
            end
        end
    end
    CImGui.PopStyleVar()
end
Gnimuc commented 1 year ago

You could use PushID() / PopID().

FaresX commented 1 year ago
function edit(insbuf::InstrBuffer, addr)
    for qt in insbuf.quantities
        CImGui.PushID(qt.name)
        edit(qt, insbuf.instrnm, addr)
        CImGui.PopID()
    end
end
isvalidaddr = selectaddr in append!(collect.(keys.(values(instrbuffer)))...)
selectaddr = isvalidaddr ? selectaddr : ""
selectins in keys(default_insbufs) || push!(default_insbufs, selectins=>InstrBuffer(selectins))
insbuf = selectaddr == "" ? default_insbufs[selectins] : instrbuffer[selectins][selectaddr]
CImGui.PushID(string(selectins, selectaddr))
edit(insbuf, selectaddr)
CImGui.PopID()

image I have tried it. There exists the same problem and I think it is not because of the IDs. When I do not select any addresses, there is no problem. Here, "instrbuffer" is a global variable in my module, and "default_insbufs" is a local variable inside a let block.

Gnimuc commented 1 year ago

Please submit an MWE, I'm not responsible for debugging your incomplete code with such limited info.

FaresX commented 1 year ago

I have fixed this issue, but I am not clear about the reason. According to your suggestion, I wrote the sample code below. When I follow the workflow that generates data remotely and fetches back, the problem will be reproduced. However, when all things happen locally, there will be no problem.

module Example

using CImGui
using CImGui.CSyntax
using CImGui.CSyntax.CStatic
using CImGui.GLFWBackend
using CImGui.OpenGLBackend
using CImGui.GLFWBackend.GLFW
using CImGui.OpenGLBackend.ModernGL
using CImGui.LibCImGui
using Distributed

function ui()
    glsl_version = 130
    GLFW.WindowHint(GLFW.CONTEXT_VERSION_MAJOR, 3)
    GLFW.WindowHint(GLFW.CONTEXT_VERSION_MINOR, 0)
    error_callback(err::GLFW.GLFWError) = @error "GLFW ERROR: code $(err.code) msg: $(err.description)"

    # setup GLFW error callback
    GLFW.SetErrorCallback(error_callback)
    # create window
    window = GLFW.CreateWindow(1920, 1080, "Example")
    @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()

    # setup Platform/Renderer bindings
    ImGui_ImplGlfw_InitForOpenGL(window, true)
    ImGui_ImplOpenGL3_Init(glsl_version)

    @async try
        p_open = true
        while !GLFW.WindowShouldClose(window)
            GLFW.PollEvents()
            ImGui_ImplOpenGL3_NewFrame()
            ImGui_ImplGlfw_NewFrame()
            CImGui.NewFrame()

            p_open && @c showexample(&p_open)

            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 showexample(p_open::Ref)
    CImGui.SetNextWindowPos((600, 100), CImGui.ImGuiCond_Once)
    CImGui.SetNextWindowSize((1000, 800), CImGui.ImGuiCond_Once)
    if CImGui.Begin("Example", p_open)
        CImGui.Button("Add Test") && (add_remote(); add_local())
        # CImGui.Button("Add Test") && push!(testlist, Test())
        for (i, test) in enumerate(testlist)
            CImGui.PushID(i)
            edit(test)
            CImGui.PopID()
        end
        CImGui.End()
    end
end

mutable struct Test
    input1
    input2
end

Test() = Test("\0"^16, "\0"^16)

function edit(t::Test)
    CImGui.InputTextWithHint("Input1", "Input1", t.input1, length(t.input1)); CImGui.SameLine()
    CImGui.InputTextWithHint("Input2", "Input2", t.input2, length(t.input2))
end

testlist = []

function add_remote()
    remotecall_wait(workers()[1]) do 
        push!(testlist, Test())     
    end
end

function add_local()
    testlist_remote = remotecall_fetch(()->testlist, workers()[1]) |> deepcopy
    global testlist = testlist_remote
end

end # Example

Example.ui()
FaresX commented 1 year ago

Just a little while ago, I found that the problem is the using of deepcopy instead of remote_do.

JamesWrigley commented 1 month ago

I'm gonna close this since it looks like the problem is solved.