JuliaGraphics / QML.jl

Build Qt6 QML interfaces for Julia programs.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Properties passed to `loadqml` makes app to crash when loaded from module

JanisErdmanis opened this issue · comments

In the current master version of QML, in a situation where an application code is placed within a module exposing a julia_main function to start the application, there happens to be a crash when this julia_main function is called. As an example, consider the following:

module QMLApp

using QML


const _PROPERTIES = JuliaPropertyMap(
    "text" => "Hello World Again!",
    "count" => 16
)


function julia_main()::Cint

    loadqml((@__DIR__) * "/App.qml"; _PROPERTIES)
    exec()

    return 0
end

end # module QMLApp

When called from a module with using QMLApp; QMLApp.julia_main() a following error happens:

ERROR: C++ object of type N7qmlwrap16JuliaPropertyMapE was deleted
Stacktrace:
  [1] cxxupcast
    @ ~/.julia/packages/CxxWrap/aXNBY/src/CxxWrap.jl:624 [inlined]
  [2] cxxupcast
    @ ~/.julia/packages/CxxWrap/aXNBY/src/CxxWrap.jl:274 [inlined]
  [3] cxxupcast
    @ ~/.julia/packages/CxxWrap/aXNBY/src/CxxWrap.jl:277 [inlined]
  [4] convert
    @ ~/.julia/packages/CxxWrap/aXNBY/src/CxxWrap.jl:682 [inlined]
  [5] cxxconvert
    @ ~/.julia/packages/CxxWrap/aXNBY/src/CxxWrap.jl:582 [inlined]
  [6] cconvert
    @ ~/.julia/packages/CxxWrap/aXNBY/src/CxxWrap.jl:558 [inlined]
  [7] _set_context_property
    @ ~/.julia/packages/CxxWrap/aXNBY/src/CxxWrap.jl:624 [inlined]
  [8] set_context_property(ctx::QML.QQmlContextDereferenced, name::String, value::QML._JuliaPropertyMapAllocated)
    @ QML ~/.julia/packages/CxxWrap/aXNBY/src/CxxWrap.jl:61
  [9] set_context_property(ctx::CxxWrap.CxxWrapCore.CxxPtr{QML.QQmlContext}, name::String, jpm::QML.JuliaPropertyMap)
    @ QML ~/.julia/packages/CxxWrap/aXNBY/src/CxxWrap.jl:408
 [10] loadqml(qmlfilename::String; kwargs::Base.Pairs{Symbol, QML.JuliaPropertyMap, Tuple{Symbol}, NamedTuple{(:_PROPERTIES,), Tuple{QML.JuliaPropertyMap}}})
    @ QML ~/.julia/packages/QML/dFnTL/src/QML.jl:90
 [11] loadqml
    @ ~/.julia/packages/QML/dFnTL/src/QML.jl:86 [inlined]
 [12] julia_main()
    @ QMLApp ~/BtSync/PeaceFounder/SandBox/QMLApp/src/QMLApp.jl:14
 [13] top-level scope
    @ REPL[2]:1

This error does not happen if the _PROPERTIES are not passed to loadqml. Also, interestingly, the code works fine when the module is included within a file like include("src/QMLApp.jl"); QMLApp.julia_main().

A full self-contained example is available at https://github.com/JanisErdmanis/QMLAppTest/tree/julia_main_bug

Can you replicate the issue with QML 0.8?

Yes, I just tested it with Julia 1.9.2 and QML 0.8. The error is still there.

Which operating system are you using?

MacOS 14 on Apple silicon. I will check it later on Ubuntu.

I can report that the problem persists when using Ubuntu 22.10 with Julia 1.9.3 and the current QML 0.8 version.

The reason for this is that _PROPERTIES actually stores a pointer to a C++ object, so it stores a pointer during precompilation that is then used in a later run, but of course no longer valid. To work around that you can return _PROPPERTIES from a function instead of making it a module constant.

The new documentation at https://juliagraphics.github.io/QML.jl/dev/#Using-QML.jl-inside-another-Julia-module should make this clearer, so I'm closing this.

This makes sense. Another workaround I just tested is that one can define the relevant global variables in the __init__ block:

module QMLApp

using QML

function __init__()

    global PROPERTIES = JuliaPropertyMap(
        "text" => "Hello World Again!",
        "count" => 16
    )

end

function julia_main()::Cint

    loadqml((@__DIR__) * "/App.qml"; PROPERTIES)
    exec()

    return 0
end

end 

This makes it easier to structure larger applications where passing multiple properties to every function would be cumbersome. It also seems necessary for processing signals on the Julia side, which updates the model and ultimately from which QML redraws the view.

Indeed, good solution!