QML.jl

repository·main·Indexed 19 days ago

https://github.com/juliagraphics/qml.jl

A high-performance interface between Julia and Qt6/Qt5 QML for creating graphical user interfaces. It supports loading QML via QQmlApplicationEngine, QQuickView, and QQmlComponent, and enables interaction through Observables, JuliaItemModels, and the @qmlfunction macro. Features include bi-directional updates, signal emission via @emit, and specialized rendering types like JuliaDisplay and JuliaCanvas.

Tokens
4K
Snippets
13
Records
19
Agent score
65%

What's inside QML.jl

  1. Interact between Julia and QML

    main

    You can bridge Julia and QML using several mechanisms:

    • Call Julia functions from QML: Use the @qmlfunction macro to expose Julia functions to the QML environment.
    • Manage Context Properties: Read or set properties in the QML context from Julia using keywords in loadqml or by using set_context_property.
    • Emit Signals: Use the @emit macro to send signals from Julia to QML.
    • Use Data Models: Implement data models in Julia using JuliaItemModel or JuliaPropertyMap to be consumed by QML.
    • Connect Signals directly: You can connect a Julia function directly to a QML signal within the QML code (e.g., connecting to a QTimer signal).
  2. Understand type conversion between Julia and QML

    main

    Most fundamental types are converted implicitly between Julia and QML.

    Important Notes:

    • Integers: The default integer type in QML corresponds to Int32 in Julia.
    • Maps: QVariantMap is converted such that you can use the Julia indexing operator [] with string keys to access elements. This is particularly useful for arguments passed to the QML append function in list models.
  3. Run QML applications with an active REPL using exec_async

    main

    When using the exec command to launch an application, the Julia REPL will block until the GUI is closed. If you need to continue using the REPL while the GUI is active, use exec_async.

    exec_async keeps the REPL active by using a timer in the Julia event loop to periodically poll the QML interface for events.

    include("repl-background.jl")
    plot([1,2],[3,4])
  4. Enable bi-directional updates with Observables

    main

    By passing an Observable (from Observables.jl) as a context property in loadqml, you enable bi-directional change notification between Julia and QML.

    When the value changes in QML (e.g., via a Slider), the Julia Observable is updated. Conversely, updating the Observable in Julia (e.g., input[] = 3.0) will update the UI in QML.

    using QML
    using Observables
    
    const qml_file = "observable.qml"
    const input = Observable(1.0)
    const output = Observable(0.0)
    
    # Run the application
    loadqml(qml_file, input=input, output=output)
    exec_async()
  5. Migration guide: Upgrade from v0.9 to v0.11

    main

    If you are upgrading from older versions, note the following breaking changes:

    From v0.9 to v0.10:

    • The QML module name changed from org.julialang to jlqml. Replace import org.julialang with import jlqml in all QML files.
    • Makie support moved to the QMLMakie.jl package. Install it via add QMLMakie.

    From v0.10 to v0.11:

    • Qt enum types (e.g., QML.Orientation) are now mapped to complete Julia @enum types. They are no longer automatically treated as Integer, which may break code relying on integer comparisons.
  6. Submit changes via a pull request

    main

    To contribute changes to QML.jl (code or documentation):

    1. Ensure your local fork is synchronized with the main branch of the upstream QML.jl repository.
    2. Push your changes to your local fork on GitHub.
    3. Click the "Contribute" button on your fork's GitHub page to create a pull request.
  7. Use QML.jl inside another Julia module

    main

    When wrapping QML.jl inside a custom Julia module, you must handle precompilation and the lifecycle of the QML engine. Because the engine is destroyed between runs, you must avoid storing invalid pointers and ensure all @qmlfunction macros are called again before restarting the program.

    Best Practices:

    1. Use absolute paths for QML files to avoid issues if the working directory is overridden.
    2. Define JuliaPropertyMap via a function rather than a const value, as the property map is a pointer.
    3. Use a setup function to call all relevant @qmlfunction macros before loading the QML file.
    module QmlModuleTest
    
    using QML
    # Use absolute path to avoid working directory issues
    const qml_file = joinpath(dirname(@__FILE__), "qml", "frommodule.qml")
    
    # Propertymap is a pointer; use a function, not a const
    props() = JuliaPropertyMap(
        "hi" => "Hi",
    )
    
    world() = " world!"
    
    # Function to re-register macros
    function define_funcs()
      @qmlfunction world
    end
    
    function main()
      define_funcs()
      loadqml(qml_file; props=props())
      exec()
    end
    
    end # module QmlModuleTest
  8. How to load QML files

    main

    QML.jl supports three primary methods for loading QML files, mirroring the corresponding Qt classes:

    1. QQmlApplicationEngine (via loadqml)

    The easiest way to run a main.qml file. It creates and returns a QQmlApplicationEngine. The engine's lifetime is managed by C++, so you don't need to keep a reference to prevent garbage collection.

    2. QQuickView

    Creates a window directly, so you don't need to wrap your QML in an ApplicationWindow.

    3. QQmlComponent

    Allows loading QML code from a Julia string wrapped in a QByteArray.

    ### QQmlApplicationEngine
    ```julia
    using QML
    loadqml("main.qml")
    exec()

    QQuickView

    qview = init_qquickview()
    set_source(qview, "main.qml")
    QML.show(qview)
    exec()

    QQmlComponent

    qml_data = QByteArray("""
    import ...
    
    ApplicationWindow {
      ...
    }
    """)
    
    qengine = init_qmlengine()
    qcomp = QQmlComponent(qengine)
    set_data(qcomp, qml_data, "")
    create(qcomp, qmlcontext())
    
    exec()
  9. Build the QML.jl documentation locally

    main

    To build and view the documentation on your local machine, follow these steps. This assumes you are using a bash terminal (on Windows, use the VSCode bash terminal or Windows Terminal if using juliaup).

    1. Install TestEnv: Add TestEnv to your global Julia environment.
    2. Fork and Clone: Fork the QML.jl repository on GitHub and clone your fork locally.
    3. Setup Project: Navigate to the QML.jl folder, start Julia with the --project flag, and instantiate the project.
    4. Activate Test Environment: Use TestEnv to activate the development environment.
    5. Serve Docs: Use LiveServer to build and serve the documentation.

    View the documentation at http://localhost:8080.

    # 1. Install TestEnv globally
    using Pkg
    Pkg.add("TestEnv")
    
    # --- In your terminal ---
    # git clone https://github.com/USERNAME/QML.jl.git
    # cd QML.jl
    # julia --project
    
    # 2. Inside the Julia REPL (with --project active)
    using Pkg
    Pkg.instantiate()
    
    using TestEnv
    TestEnv.activate()
    
    using LiveServer
    servedocs()
  10. Load QML files using different engines

    main

    QML.jl provides three methods for loading QML files, mirroring the corresponding Qt classes. Choose the method based on your requirements:

    1. QML.QQmlApplicationEngine: Use the loadqml function for a standard application engine approach.
    2. QML.QQuickView: Use this if you want to create a window directly without needing to wrap your QML code in an ApplicationWindow component.
    3. QQmlComponent: Use this if you need to run QML code that is contained within a string rather than a file.