Board Game interface which library?

I wan to program some kind of board game but I don’t know how to do the graphics. I want some simple mechanic to show unicode symbols at 2d-coordinates and process mouse and keyboard events.
What I found so far are wrapper for low level things like OpenGL or GUI things like GTK3+.
Can you recommend me some library and maybe even give me some hints to how to achieve what want?
Thanks.

I want some simple mechanic to show unicode symbols at 2d-coordinates and process mouse and keyboard events.

If that’s really the only thing you need, GLVisualize should work pretty well (note it’s Julia 0.5 only right now)!

Checkout this example, showing unicode text and control the position/scale/color of each character:

The GLVisualize window offers all kind of Reactive signals for mouse/keyboard:

julia> using GLVisualize

julia> w = glscreen()
name: GLVisualize
children: 0
Inputs:
  framebuffer_size => Reactive.Signal{FixedSizeArrays.Vec{2,Int64}}
  buttons_pressed => Reactive.Signal{Set{Int64}}
  scroll => Reactive.Signal{FixedSizeArrays.Vec{2,Float64}}
  hasfocus => Reactive.Signal{Bool}
  keyboard_buttons => Reactive.Signal{Tuple{Int64,Int64,Int64,Int64}}
  button_down => Reactive.Signal{Int64}
  mouseinside => Reactive.Signal{Bool}
  window_size => Reactive.Signal{FixedSizeArrays.Vec{2,Int64}}
  mouse_buttons_pressed => Reactive.Signal{Set{Int64}}
  mouse_button_down => Reactive.Signal{Int64}
  dropped_files => Reactive.Signal{Array{String,1}}
  arrow_navigation => Reactive.Signal{Symbol}
  button_released => Reactive.Signal{Int64}
  unicode_input => Reactive.Signal{Array{Char,1}}
  cursor_position => Reactive.Signal{FixedSizeArrays.Vec{2,Float64}}
  window_area => Reactive.Signal{GeometryTypes.SimpleRectangle{Int64}}
  mouseposition => Reactive.Signal{FixedSizeArrays.Vec{2,Float64}}
  key_pressed => Reactive.Signal{Bool}
  window_open => Reactive.Signal{Bool}
  mouse2id => Reactive.Signal{GLWindow.SelectionID{Int64}}
  mouse_buttons => Reactive.Signal{Tuple{Int64,Int64,Int64}}
  entered_window => Reactive.Signal{Bool}
  window_position => Reactive.Signal{FixedSizeArrays.Vec{2,Int64}}
  mouse_button_released => Reactive.Signal{Int64}

You can access those via e.g. window.inputs[:mouse_buttons].

How to work with Reactive signals is already shown in the text example.
You can learn more about it at Introduction - Reactive.jl

3 Likes

Thank you very very much :slight_smile:
I will look into this and maybe I’ll come back with questions.

Couldn’t resist taking a stab at this using QML.jl. Below is the Julia and QML code for a 2D canvas with draggable emoji. There is also a variant in the QML examples that uses a regular grid layout. Julia code:

using QML

# Represents the state related to a single emoji
type EmojiState
  emoji::String
  numclicks::Float64
  bgcolor::String
  ex::Float64
  ey::Float64
end

# Build a list of emoji, positioned randomly
emoji = EmojiState[]
randpos() = rand()*0.8+0.1
for (i,e) in enumerate(["😁", "😃", "😆", "😎", "😈", "☹", "🌚", "😤", "🐭"])
  push!(emoji, EmojiState(e,0, i%2 == 0 ? "lightgrey" : "darkgrey", randpos(), randpos()))
end
emojiModel = ListModel(emoji) # passed to QML

cols = 3

# path to the QML file
qml_file = "board.qml"

# create the app, with cols and emojiModel exposed as QML context properties
@qmlapp qml_file cols emojiModel

# Start the GUI
exec()

# Print the click summary after exit
for e in emoji
  println("$(e.emoji) was clicked $(Int(e.numclicks)) times")
end

QML code:

import QtQuick 2.0
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import org.julialang 1.0

ApplicationWindow {
  id: appRoot
  title: "Arrays"
  width: 300
  height: 300
  visible: true

  Rectangle {
    id: board
    width: appRoot.width
    height: appRoot.height
    color: "darkgreen"
    
    Repeater { // Repeat for each emoji
      anchors.fill: parent
      model: emojiModel

      Rectangle { // Each emoji is a rectangle with text in the center
        width: appRoot.width/(4*cols)
        height: appRoot.height/(4*cols)
        color: bgcolor // from emoji state
        x: ex*appRoot.width
        y: ey*appRoot.height
        Text {
          anchors.centerIn: parent
          font.pixelSize: 0.75*appRoot.width/(4*cols)
          text: emoji // from emoji state
        }

        MouseArea {
          anchors.fill: parent
          drag.target: parent
          onClicked: numclicks += 1 // updates the clicks on the Julia side
          onPressed: parent.color = "white" // visual feedback for the clicking
          onReleased: parent.color = bgcolor
        }
      }
    }
  }
}

The above example assumes QML is installed (Pkg.add("QML"), tested on latest release) the julia code and the QML file named board.qml are in the Julia working directory. Then running Julia with the .jl as argument or including it should bring up the GUI. Screenshot from macOS (which brings up color emoji by default):

Both this example and the regular grid example are in the example directory on QML.jl master, but they run also on the latest released version.

4 Likes

Nice, thank you.
Unfortunately I can’t try it. On Julia 0.5 CxxWrap does not work saying

julia> Pkg.test("CxxWrap")
INFO: Testing CxxWrap
running test containers.jl...
ERROR: LoadError: UndefVarError: _l_jlcxx not defined
 in include_from_node1(::String) at ./loading.jl:488
 in macro expansion; at ./none:2 [inlined]
 in anonymous at ./<missing>:?
 in eval(::Module, ::Any) at ./boot.jl:234
 in process_options(::Base.JLOptions) at ./client.jl:242
 in _start() at ./client.jl:321
while loading /home/jk/.julia/v0.5/CxxWrap/src/CxxWrap.jl, in expression starting on line 27
ERROR: LoadError: LoadError: Failed to precompile CxxWrap to /home/jk/.julia/lib/v0.5/CxxWrap.ji.
 in compilecache(::String) at ./loading.jl:593
 in require(::Symbol) at ./loading.jl:422
 in include_from_node1(::String) at ./loading.jl:488
 in macro expansion; at /home/jk/.julia/v0.5/CxxWrap/test/runtests.jl:14 [inlined]
 in anonymous at ./<missing>:?
 in include_from_node1(::String) at ./loading.jl:488
 in process_options(::Base.JLOptions) at ./client.jl:265
 in _start() at ./client.jl:321
while loading /home/jk/.julia/v0.5/CxxWrap/test/containers.jl, in expression starting on line 1
while loading /home/jk/.julia/v0.5/CxxWrap/test/runtests.jl, in expression starting on line 11
=============================================================================================[ ERROR: CxxWrap ]=============================================================================================

failed process: Process(`/home/jk/Downloads/julia-f4c6c9d4bb/bin/julia -Cx86-64 -J/home/jk/Downloads/julia-f4c6c9d4bb/lib/julia/sys.so --compile=yes --depwarn=yes --check-bounds=yes --code-coverage=none --color=yes --compilecache=yes /home/jk/.julia/v0.5/CxxWrap/test/runtests.jl`, ProcessExited(1)) [1]

============================================================================================================================================================================================================
ERROR: CxxWrap had test errors
 in #test#61(::Bool, ::Function, ::Array{AbstractString,1}) at ./pkg/entry.jl:748
 in (::Base.Pkg.Entry.#kw##test)(::Array{Any,1}, ::Base.Pkg.Entry.#test, ::Array{AbstractString,1}) at ./<missing>:0
 in (::Base.Pkg.Dir.##2#3{Array{Any,1},Base.Pkg.Entry.#test,Tuple{Array{AbstractString,1}}})() at ./pkg/dir.jl:31
 in cd(::Base.Pkg.Dir.##2#3{Array{Any,1},Base.Pkg.Entry.#test,Tuple{Array{AbstractString,1}}}, ::String) at ./file.jl:59
 in #cd#1(::Array{Any,1}, ::Function, ::Function, ::Array{AbstractString,1}, ::Vararg{Array{AbstractString,1},N}) at ./pkg/dir.jl:31
 in (::Base.Pkg.Dir.#kw##cd)(::Array{Any,1}, ::Base.Pkg.Dir.#cd, ::Function, ::Array{AbstractString,1}, ::Vararg{Array{AbstractString,1},N}) at ./<missing>:0
 in #test#3(::Bool, ::Function, ::String, ::Vararg{String,N}) at ./pkg/pkg.jl:258
 in test(::String, ::Vararg{String,N}) at ./pkg/pkg.jl:258

On Julia 0.6 CxxWrap works but QML does not:

julia> Pkg.test("QML")
INFO: Testing QML
running test functions.jl...
running test julia_arrays.jl...
julia_array = Any["A", "TEST2", "Added", 2, 3]
move_array = [0, 1, 5, 6, 7, 2, 3, 4, 8, 9]
custom_list = ListElem[ListElem("a", 1), ListElem("b", 5), ListElem("ten", 10)]
running test julia_object.jl...
running test julia_signal.jl...
running test libinfo.jl...
qt_prefix_path() = "/home/jk/anaconda2"
running test listviews.jl...
QXcbIntegration: Cannot create platform OpenGL context, neither GLX nor EGL are enabled
Failed to create OpenGL context for format QSurfaceFormat(version 3.3, options QFlags(), depthBufferSize 24, redBufferSize -1, greenBufferSize -1, blueBufferSize -1, alphaBufferSize -1, stencilBufferSize 8, samples -1, swapBehavior 2, swapInterval 1, profile  1) 

signal (6): Abgebrochen
while loading /home/jk/.julia/v0.6/QML/test/listviews.jl, in expression starting on line 40
raise at /build/glibc-9tT8Do/glibc-2.23/signal/../sysdeps/unix/sysv/linux/raise.c:54
abort at /build/glibc-9tT8Do/glibc-2.23/stdlib/abort.c:89
_ZNK14QMessageLogger5fatalEPKcz at /home/jk/anaconda2/lib/libQt5Core.so.5 (unknown line)
_ZN13QSGRenderLoop28handleContextCreationFailureEP12QQuickWindowb at /home/jk/anaconda2/lib/libQt5Quick.so.5 (unknown line)
unknown function (ip: 0x7f43cfbb81a4)
unknown function (ip: 0x7f43cfbb8c7f)
_ZN7QWindow5eventEP6QEvent at /home/jk/anaconda2/lib/libQt5Gui.so.5 (unknown line)
_ZN12QQuickWindow5eventEP6QEvent at /home/jk/anaconda2/lib/libQt5Quick.so.5 (unknown line)
_ZN19QApplicationPrivate13notify_helperEP7QObjectP6QEvent at /home/jk/anaconda2/lib/libQt5Widgets.so.5 (unknown line)
_ZN12QApplication6notifyEP7QObjectP6QEvent at /home/jk/anaconda2/lib/libQt5Widgets.so.5 (unknown line)
_ZN16QCoreApplication15notifyInternal2EP7QObjectP6QEvent at /home/jk/anaconda2/lib/libQt5Core.so.5 (unknown line)
_ZN22QGuiApplicationPrivate18processExposeEventEPN29QWindowSystemInterfacePrivate11ExposeEventE at /home/jk/anaconda2/lib/libQt5Gui.so.5 (unknown line)
_ZN22QGuiApplicationPrivate24processWindowSystemEventEPN29QWindowSystemInterfacePrivate17WindowSystemEventE at /home/jk/anaconda2/lib/libQt5Gui.so.5 (unknown line)
_ZN22QWindowSystemInterface22sendWindowSystemEventsE6QFlagsIN10QEventLoop17ProcessEventsFlagEE at /home/jk/anaconda2/lib/libQt5Gui.so.5 (unknown line)
unknown function (ip: 0x7f43c40b6a3f)
g_main_dispatch at /home/ilan/minonda/conda-bld/glib_1484870019781/work/glib-2.50.2/glib/gmain.c:3203 [inlined]
g_main_context_dispatch at /home/ilan/minonda/conda-bld/glib_1484870019781/work/glib-2.50.2/glib/gmain.c:3856
g_main_context_iterate at /home/ilan/minonda/conda-bld/glib_1484870019781/work/glib-2.50.2/glib/gmain.c:3929
g_main_context_iteration at /home/ilan/minonda/conda-bld/glib_1484870019781/work/glib-2.50.2/glib/gmain.c:3990
_ZN20QEventDispatcherGlib13processEventsE6QFlagsIN10QEventLoop17ProcessEventsFlagEE at /home/jk/anaconda2/lib/libQt5Core.so.5 (unknown line)
_ZN10QEventLoop4execE6QFlagsINS_17ProcessEventsFlagEE at /home/jk/anaconda2/lib/libQt5Core.so.5 (unknown line)
_ZN16QCoreApplication4execEv at /home/jk/anaconda2/lib/libQt5Core.so.5 (unknown line)
_ZN7qmlwrap18ApplicationManager4execEv at /home/jk/.julia/v0.6/QML/deps/usr/lib/libqmlwrap.so (unknown line)
_ZN8cxx_wrap6detail11CallFunctorIvIEE5applyEPKv at /home/jk/.julia/v0.6/QML/deps/usr/lib/libqmlwrap.so (unknown line)
exec at ./<missing>:0
unknown function (ip: 0x7f43d497aeff)
jl_call_fptr_internal at /home/centos/buildbot/slave/package_tarball64/build/src/julia_internal.h:337 [inlined]
jl_call_method_internal at /home/centos/buildbot/slave/package_tarball64/build/src/julia_internal.h:356 [inlined]
jl_apply_generic at /home/centos/buildbot/slave/package_tarball64/build/src/gf.c:1930
do_call at /home/centos/buildbot/slave/package_tarball64/build/src/interpreter.c:75
eval at /home/centos/buildbot/slave/package_tarball64/build/src/interpreter.c:242
jl_interpret_toplevel_expr at /home/centos/buildbot/slave/package_tarball64/build/src/interpreter.c:34
jl_toplevel_eval_flex at /home/centos/buildbot/slave/package_tarball64/build/src/toplevel.c:575
jl_parse_eval_all at /home/centos/buildbot/slave/package_tarball64/build/src/ast.c:873
jl_load at /home/centos/buildbot/slave/package_tarball64/build/src/toplevel.c:614
include_from_node1 at ./loading.jl:569
unknown function (ip: 0x7f43d4979b82)
jl_call_fptr_internal at /home/centos/buildbot/slave/package_tarball64/build/src/julia_internal.h:337 [inlined]
jl_call_method_internal at /home/centos/buildbot/slave/package_tarball64/build/src/julia_internal.h:356 [inlined]
jl_apply_generic at /home/centos/buildbot/slave/package_tarball64/build/src/gf.c:1930
include at ./sysimg.jl:14
macro expansion at /home/jk/.julia/v0.6/QML/test/runtests.jl:17 [inlined]
anonymous at ./<missing> (unknown line)
jl_call_fptr_internal at /home/centos/buildbot/slave/package_tarball64/build/src/julia_internal.h:337 [inlined]
jl_call_method_internal at /home/centos/buildbot/slave/package_tarball64/build/src/julia_internal.h:356 [inlined]
jl_toplevel_eval_flex at /home/centos/buildbot/slave/package_tarball64/build/src/toplevel.c:587
jl_parse_eval_all at /home/centos/buildbot/slave/package_tarball64/build/src/ast.c:873
jl_load at /home/centos/buildbot/slave/package_tarball64/build/src/toplevel.c:614
include_from_node1 at ./loading.jl:569
unknown function (ip: 0x7f43ecf96ccb)
jl_call_fptr_internal at /home/centos/buildbot/slave/package_tarball64/build/src/julia_internal.h:337 [inlined]
jl_call_method_internal at /home/centos/buildbot/slave/package_tarball64/build/src/julia_internal.h:356 [inlined]
jl_apply_generic at /home/centos/buildbot/slave/package_tarball64/build/src/gf.c:1930
include at ./sysimg.jl:14
unknown function (ip: 0x7f43ece2d9eb)
jl_call_fptr_internal at /home/centos/buildbot/slave/package_tarball64/build/src/julia_internal.h:337 [inlined]
jl_call_method_internal at /home/centos/buildbot/slave/package_tarball64/build/src/julia_internal.h:356 [inlined]
jl_apply_generic at /home/centos/buildbot/slave/package_tarball64/build/src/gf.c:1930
process_options at ./client.jl:305
_start at ./client.jl:371
unknown function (ip: 0x7f43ecfa2898)
jl_call_fptr_internal at /home/centos/buildbot/slave/package_tarball64/build/src/julia_internal.h:337 [inlined]
jl_call_method_internal at /home/centos/buildbot/slave/package_tarball64/build/src/julia_internal.h:356 [inlined]
jl_apply_generic at /home/centos/buildbot/slave/package_tarball64/build/src/gf.c:1930
jl_apply at /home/centos/buildbot/slave/package_tarball64/build/ui/../src/julia.h:1422 [inlined]
true_main at /home/centos/buildbot/slave/package_tarball64/build/ui/repl.c:127
main at /home/centos/buildbot/slave/package_tarball64/build/ui/repl.c:264
__libc_start_main at /build/glibc-9tT8Do/glibc-2.23/csu/../csu/libc-start.c:291
unknown function (ip: 0x40170c)
Allocations: 4054219 (Pool: 4052624; Big: 1595); GC: 5
===============================================================================================[ ERROR: QML ]===============================================================================================

failed process: Process(`/home/jk/Downloads/julia-68e911be53/bin/julia -Cx86-64 -J/home/jk/Downloads/julia-68e911be53/lib/julia/sys.so --compile=yes --depwarn=yes --check-bounds=yes --code-coverage=none --color=yes --compilecache=yes /home/jk/.julia/v0.6/QML/test/runtests.jl`, ProcessSignaled(6)) [0]

============================================================================================================================================================================================================
ERROR: QML had test errors

Any pointers on how to proceed?

Regarding the CxxWrap problem on 0.5: looks like it didn’t build correctly, what is the output of Pkg.build("CxxWrap")?

The problem on 0.6 looks like Qt having trouble finding OpenGL for some reason. I have this too on Fedora, where I have to start julia using:

LD_PRELOAD=/usr/lib64/libglvnd/libGL.so.1 julia

The location of your actual libGL may vary of course.

1 Like

Building CxxWrap helped, the test runs without problems.
The preload thing doesn’t help though. I have Qt5 and OpenGl installed, do I need to install other software?

Normally that should be sufficient, the next thing to try to debug this would be running a qml app outside of Julia. Does

qmlscene $HOME/.julia/v0.6/QML/test/qml/qquickview.qml

bring up a window briefly and then error out with “ReferenceError: hi is not defined”?

Also, to avoid cluttering the forum I have created an issue to debug this further.