Qt Bindings
Tangible engine enables users to take advantage of specially engineered physical objects that can be recognized by Ideum touch-enabled products. The objects, called tangibles, are made of conductive material that mimics touches on the surface of a p-cap display. On the software end, the Tangible Engine is a windows service that runs in the background, awaiting the connection of client apps that require tangible recognition capabilities. A service model was chosen to enable Ideum to create and deploy fixes and new features without requiring clients to rebuild their own apps. The Qt bindings use a TCP socket to communicate with a locally running service.
Setting up the Engine
The TangibleClient
class is designed to be set as a context property, so that it can be accessed directly in QML. To do this, create an instance of a TangibleClient
in main.cpp
, set it as a context property in the engine, and call its setup call. The TangibleClient
will setup a TCP connection, connect to the service, request a patterns list, sends updates each frame, and updates the QAbstractList
of tangibles. TangibleClient
may be constructed with an optional boolean as a third argument to enable or disable logging; this boolean defaults to true.
Note: The port value (ie. 4949
) passed to the TangibleClient
constructor must match the port being used by the Tangible Engine Service, or a connection cannot be made.
The TangibleSimulator
is an optional class that can be used as a drop-in replacement for TangibleClient
. It contains the same interface, and should also be registered in the root context as TangibleEngine
. It will not provide functionality on its own - rather, if the Qml project also includes the TangibleSimulator component, this component will become active and enable users to place simulated tangibles in the scene. In the main.cpp below, a preprocessor directive of #define USE_TANGIBLE_SIMULATOR
is used as a toggle for this functionality.
#include <TangibleEngine/tangibleclient.h>
#include <TangibleEngine/tangiblesimulator.h>
#define USE_TANGIBLE_SIMULATOR
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
app.processEvents();
app.setQuitOnLastWindowClosed(true);
#ifdef USE_TANGIBLE_SIMULATOR
///Instantiate a Tangible Client with the simulator class.
TangibleSimulator * client = new TangibleSimulator();
#else
///Instantiate a Tangible Client with the desired port.
TangibleClient * client = new TangibleClient("Localhost", 4949);
#endif
QQmlApplicationEngine engine;
///Register type Tangible with Engine
qmlRegisterType<Tangible>("Tangibles",2,0,"Tangible");
///Set the client instance as a context property
engine.rootContext()->setContextProperty("TangibleEngine", client);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QWindow *main_window = qobject_cast<QWindow*>(engine.rootObjects().first());
///Call the client setup method, passing the main_window of the application
client->setup(main_window);
return app.exec();
}
Interfacing with the Engine
Interfacing with the Engine through qml is very simple. With the instance of TangibleClient
set as a context property in the qml engine, all you need to do is reference the QAbstractList
titled tangibles. In this example, we use the tangibles list as the model for a Repeater
of TangibleVisual.qml
components.
import QtQuick 2.9
import QtQuick.Window 2.2
import "TangibleEngine"
Window {
id: mainWindow
visibility: "Windowed"
visible: true
Item
{
focus: true
Keys.onEscapePressed: Qt.quit()
}
///This repeater is populated with the Tangible Engine QAbstractListModel and will update
/// as tangibles are placed, moved, and removed.
Repeater
{
model: TangibleEngine.tangibles
TangibleVisual { tangible: model.tangible }
}
///This component will only be visible if TangibleEngine.isSimulator is true.
TangibleSimulator {}
}
The TangibleVisual.qml
component handles the visual representation of the recognized tangible, including its placement location, rotation, and other related information.
The TangibleSimulator.qml
component resides in the TangibleEngine directory (note the import "TangibleEngine"
directive). If included, it should reside at the top level above other components within the window context - it will auto-fill to the window's area and rescale itself based on a design resolution of 1920x1080. It will only be active if TangibleEngine.isSimulator is true; this is the case for the TangibleSimulator
C++ class referenced above.