Is there any way to list all object members/properties in QML & Qt 5.1?
Such as:
var obj=myQObject;
console.log(obj)
// expected output:
// obj { x:123..... }
This would be very helpful for debugging.
Straight javascript offers what you are looking for:
JSON.stringify(anything)
It works on QML items such as Rectangle, and it also works on most arbitrary objects!
Converting an object to a string
With meta objects you can debug all properties of any QML obj (i.e. QQuickItem).
You need some C++ to get the meta object of a QML component and get back property names and values as text in QML.
First you create a QMLDebugger class in C++ with a properties method:
QString QMLDebugger::properties(QQuickItem *item, bool linebreak)
{
const QMetaObject *meta = item->metaObject();
QHash<QString, QVariant> list;
for (int i = 0; i < meta->propertyCount(); i++)
{
QMetaProperty property = meta->property(i);
const char* name = property.name();
QVariant value = item->property(name);
list[name] = value;
}
QString out;
QHashIterator<QString, QVariant> i(list);
while (i.hasNext()) {
i.next();
if (!out.isEmpty())
{
out += ", ";
if (linebreak) out += "\n";
}
out.append(i.key());
out.append(": ");
out.append(i.value().toString());
}
return out;
}
This function can be static or instanceiable, doesn't matter. QML does not support exporting static methods from C++ to QML anyway. I use the header:
public:
Q_INVOKABLE static QString properties(QQuickItem *item, bool linebreak = true);
Now you export the class to QML. In you main.cpp add
#include "qmldebugger.h"
and
qmlRegisterType<QMLDebugger>("MyDemoLibrary", 1, 0, "QMLDebugger");
In your QML file import you new library, create an instance of QMLDebugger and start happy debugging:
import QtQuick 2.0
import MyDemoLibrary 1.0
Rectangle {
id: mainRectangle
width: 360
height: 360
color: "silver"
Text {
id: textElement
color: "#d71f1f"
text: qsTr("Hello World")
font.bold: true
font.italic: true
font.underline: true
style: Text.Raised
horizontalAlignment: Text.AlignHCenter
font.pointSize: 16
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
}
QMLDebugger {
id: qmlDebugger
}
Component.onCompleted: {
console.log("Debug mainRectangle:");
console.log(qmlDebugger.properties(mainRectangle));
console.log("Debug textElement:");
console.log(qmlDebugger.properties(textElement, false));
}
}
The full source code is available as a minimal Qt Creator project on: https://github.com/webmaster128/QMLDebugger
Just convert the QML/C++ component/object into JavaScript var object, and use the for-each syntax to list all the property:
function listProperty(item)
{
for (var p in item)
console.log(p + ": " + item[p]);
}
in your QML file, just call
onClicked:
{
listProperty(ItemID)
//or with this to list self properties
listProperty(this)
}
in case of any one wants to list only properties of an object, no signals nor slots you can use this
function listProperty(item)
{
for (var p in item)
{
if( typeof item[p] != "function" )
if(p != "objectName")
console.log(p + ":" + item[p]);
}
}
If you are not only interested in console-debugging, there is a program called GammaRay by KDAB (link) that lets you introspect and change all properties during the runtime of a QWidgets or QtQuick based program. Pretty neat!
I did not see a solution to iterate all properties yet. But maybe this helps you as a first step.
For every Quick Item you can print out the properties of Item:
import QtQuick 2.0
Rectangle {
width: 360
height: 360
function debugQuickItem(object) {
var properties = {
'activeFocus': object.activeFocus,
'activeFocusOnTab': object.activeFocusOnTab,
'anchors.alignWhenCentered': object.anchors.alignWhenCentered,
'anchors.baseline': object.anchors.baseline,
'anchors.baselineOffset': object.anchors.baselineOffset,
'anchors.bottom': object.anchors.bottom,
'anchors.bottomMargin': object.anchors.bottomMargin,
'anchors.centerIn': object.anchors.centerIn,
'anchors.fill': object.anchors.fill,
'anchors.horizontalCenter': object.anchors.horizontalCenter,
'anchors.horizontalCenterOffset': object.anchors.horizontalCenterOffset,
'anchors.left': object.anchors.left,
'anchors.leftMargin': object.anchors.leftMargin,
'anchors.margins': object.anchors.margins,
'anchors.right': object.anchors.right,
'anchors.rightMargin': object.anchors.rightMargin,
'anchors.top': object.anchors.top,
'anchors.topMargin': object.anchors.topMargin,
'anchors.verticalCenter': object.anchors.verticalCenter,
'anchors.verticalCenterOffset': object.anchors.verticalCenterOffset,
'antialiasing': object.antialiasing,
'baselineOffset': object.baselineOffset,
'children': object.children,
'childrenRect.height': object.childrenRect.height,
'childrenRect.width': object.childrenRect.width,
'childrenRect.x': object.childrenRect.x,
'childrenRect.y': object.childrenRect.y,
'clip': object.clip,
'data': object.data,
'enabled': object.enabled,
'focus': object.focus,
'height': object.height,
'implicitHeight': object.implicitHeight,
'implicitWidth': object.implicitWidth,
'layer.effect': object.layer.effect,
'layer.enabled': object.layer.enabled,
'layer.format': object.layer.format,
'layer.mipmap': object.layer.mipmap,
'layer.samplerName': object.layer.samplerName,
'layer.smooth': object.layer.smooth,
'layer.sourceRect': object.layer.sourceRect,
'layer.textureSize': object.layer.textureSize,
'layer.wrapMode': object.layer.wrapMode,
'opacity': object.opacity,
'parent': object.parent,
'resources': object.resources,
'rotation': object.rotation,
'scale': object.scale,
'smooth': object.smooth,
'state': object.state,
'states': object.states,
'transform': object.transform,
'transformOrigin': object.transformOrigin,
'transitions': object.transitions,
'visible': object.visible,
'visibleChildren': object.visibleChildren,
'width': object.width,
'x': object.x,
'y': object.y,
'z': object.z,
}
var out = "{ "
for (var key in properties)
{
out += "'" + key + "': " + properties[key] + ", "
}
out += "}"
return out;
}
Text {
id: textObject
anchors.centerIn: parent
text: "Hello World"
}
Component.onCompleted: console.log(debugQuickItem(textObject));
}
Output:
{ 'activeFocus': false, 'activeFocusOnTab': false, 'anchors.alignWhenCentered': true, 'anchors.baseline': QVariant(QQuickAnchorLine), 'anchors.baselineOffset': 0, 'anchors.bottom': QVariant(QQuickAnchorLine), 'anchors.bottomMargin': 0, 'anchors.centerIn': QQuickRectangle_QML_0(0x29857d0), 'anchors.fill': null, 'anchors.horizontalCenter': QVariant(QQuickAnchorLine), 'anchors.horizontalCenterOffset': 0, 'anchors.left': QVariant(QQuickAnchorLine), 'anchors.leftMargin': 0, 'anchors.margins': 0, 'anchors.right': QVariant(QQuickAnchorLine), 'anchors.rightMargin': 0, 'anchors.top': QVariant(QQuickAnchorLine), 'anchors.topMargin': 0, 'anchors.verticalCenter': QVariant(QQuickAnchorLine), 'anchors.verticalCenterOffset': 0, 'antialiasing': false, 'baselineOffset': 14, 'children': [object Object], 'childrenRect.height': 0, 'childrenRect.width': 0, 'childrenRect.x': 0, 'childrenRect.y': 0, 'clip': false, 'data': [object Object], 'enabled': true, 'focus': false, 'height': 17, 'implicitHeight': 17, 'implicitWidth': 80.5625, 'layer.effect': null, 'layer.enabled': false, 'layer.format': 6408, 'layer.mipmap': false, 'layer.samplerName': source, 'layer.smooth': false, 'layer.sourceRect': QRectF(0, 0, 0, 0), 'layer.textureSize': QSize(-1, -1), 'layer.wrapMode': 0, 'opacity': 1, 'parent': QQuickRectangle_QML_0(0x29857d0), 'resources': [object Object], 'rotation': 0, 'scale': 1, 'smooth': true, 'state': , 'states': [object Object], 'transform': [object Object], 'transformOrigin': 4, 'transitions': [object Object], 'visible': true, 'visibleChildren': [object Object], 'width': 80.5625, 'x': 139.71875, 'y': 171, 'z': 0, }
Related
I want to use the AFrame raycaster component to catch intersections with objects. I'm adding my custom objects to a GLTF model. I'm calling them "collision-shapes" and they're been used to catch collisions between gilt models and projectiles. Use case: shooting a bullet into an enemy.
The problem is that for some models it works, but for some of them it catches intersections outside the collision shape.
To position a collision shape I use a bone name the collision object should be anchored to.
My code is the following (I removed some parts to make it shorter):
<a-gltf-model src="#bird"
position="2 -75 -300"
animation-mixer
scale="1 1 1"
shape__Bone_38_08="bone: Bone_38_08; shape: box; halfExtents: 10 10 5"
shape__Bone_39_07="bone: Bone_39_07; shape: box; halfExtents: 15 10 10">
</a-gltf-model>
<a-gltf-model src="#orc" position="-2 0 -5" animation-mixer="clip: Orc.004" scale="2 2 2" rotation="0 180 0"
shape__hair_1="bone: hair_1; shape: box; halfExtents: 0.05 0.075 0.05"
shape__leg_L_1="bone: leg_L_1; shape: box; halfExtents: 0.05 0.125 0.05; offset: 0 -0.05 -0.1">
</a-gltf-model>
<a-entity camera look-controls position="0 1.6 0" wasd-controls>
<a-cursor color="gray" raycaster="objects: [data-raycastable]" ></a-cursor>
</a-entity>
The components:
AFRAME.registerComponent("shape", {
schema: {
bone: { default: "" },
shape: { default: "box", oneOf: ["box", "sphere", "cylinder"] },
offset: { type: "vec3", default: { x: 0, y: 0, z: 0 } },
orientation: { type: "vec4", default: { x: 0, y: 0, z: 0, w: 1 } },
// box
halfExtents: { type: "vec3", default: { x: 0.5, y: 0.5, z: 0.5 }, if: { shape: ["box"] } },
visible: { type: "boolean", default: true }
},
multiple: true,
init(){
const data = this.data;
const self = this;
const el = this.el;
el.addEventListener("model-loaded", function modelReady() {
el.removeEventListener("model-loaded", modelReady);
const boneDummy = document.createElement("a-entity");
self.setDummyShape(boneDummy, data);
self.boneObj = self.getBone(el.object3D, data.bone);
el.appendChild(boneDummy);
self.boneDummy = boneDummy;
});
},
setDummyShape(dummy, data) {
const shapeName = "collidable-shape";
const config = {
shapeName: data.bone,
shape: data.shape,
offset: data.offset,
halfExtents: data.halfExtents
};
dummy.setAttribute(shapeName, config);
},
getBone(root, boneName) {
let bone = root.getObjectByName(boneName);
if (!bone) {
root.traverse(node => {
const n = node;
if (n?.isBone && n.name.includes(boneName)) {
bone = n;
}
});
}
return bone;
},
inverseWorldMatrix: new THREE.Matrix4(),
boneMatrix: new THREE.Matrix4(),
tick() {
const el = this.el;
if (!el) { throw Error("AFRAME entity is undefined."); }
if (!this.boneObj || !this.boneDummy) return;
this.inverseWorldMatrix.copy(el.object3D.matrix).invert();
this.boneMatrix.multiplyMatrices(this.inverseWorldMatrix, this.boneObj.matrixWorld);
this.boneDummy.object3D.position.setFromMatrixPosition(this.boneMatrix);
}
})
AFRAME.registerComponent("collidable-shape", {
schema: {
shape: { default: "box", oneOf: ["box", "sphere", "cylinder"] },
offset: { type: "vec3", default: { x: 0, y: 0, z: 0 } },
orientation: { type: "vec4", default: { x: 0, y: 0, z: 0, w: 1 } },
// box
halfExtents: { type: "vec3", default: { x: 0.5, y: 0.5, z: 0.5 }, if: { shape: ["box"] } },
visible: { type: "boolean", default: true }
},
collistionObject: null ,
multiple:true,
init() {
const scene = this.el.sceneEl;
if (!scene) { throw Error("AFRAME scene is undefined."); }
if (scene.hasLoaded) {
this.initShape();
} else {
scene.addEventListener("loaded", this.initShape.bind(this));
}
},
initShape() {
const data = this.data;
this.el.setAttribute("data-raycastable", "");
this.el.addEventListener('mouseenter', evt => {
console.log("mouse enter", data.shape);
this.el.object3D.children[0].material.color.setHex(0x00ff00);
});
this.el.addEventListener('mouseleave', evt => {
console.log("mouse leave", data.shape);
this.el.object3D.children[0].material.color.setHex(0xff0000);
});
const scale = new THREE.Vector3(1, 1, 1);
this.el.object3D.getWorldScale(scale);
let shape;
let offset;
let orientation;
if (Object.prototype.hasOwnProperty.call(data, "offset")) {
offset = new THREE.Vector3(
data.offset.x * scale.x,
data.offset.y * scale.y,
data.offset.z * scale.z
);
}
if (Object.prototype.hasOwnProperty.call(data, "orientation")) {
orientation = new THREE.Quaternion();
orientation.copy(data.orientation);
}
switch (data.shape) {
case "box":
shape = new THREE.BoxGeometry(
data.halfExtents.x * 2 * scale.x,
data.halfExtents.y * 2 * scale.y,
data.halfExtents.z * 2 * scale.z
);
break;
}
this._applyShape(shape, offset, data.visible);
},
_applyShape(shape, offset, visible) {
const material = new THREE.MeshBasicMaterial({ color: 0xff0000, transparent: true, opacity: 0.3 });
const wireframe = new THREE.LineSegments(
new THREE.EdgesGeometry(shape),
new THREE.LineBasicMaterial({ color: 0xff0000, linewidth: 3 }));
this.collistionObject = new THREE.Mesh(shape, material);
this.collistionObject.add(wireframe);
if (offset) {
this.collistionObject.position.set(offset.x, offset.y, offset.z);
}
this.collistionObject.visible = visible === true;
this.el.setObject3D("mesh", this.collistionObject);
const size = new THREE.Vector3();
const box = new THREE.Box3().setFromObject(this.el.object3D);
box.getSize(size);
const bbox = new THREE.BoxGeometry(size.x, size.y, size.z);
const bboxWireframe = new THREE.LineSegments(
new THREE.EdgesGeometry(bbox),
new THREE.LineBasicMaterial({ color: 0x000000, linewidth: 10 }));
this.el.object3D.add(bboxWireframe)
}
});
The sample project can be found here: https://glitch.com/edit/#!/collisons-test
Please note, it works as expected for the bird, but behaves strange for the orc. Also the bounding box doesn't match the collision-shape box itself. This is also something not clear to me.
Also the bounding box doesn't match the collision-shape box itself.
The bounding box is taking the world matrix into account. You can see how it's changing when the model scale is different:
Also you can see the red boxes also aren't scaling nicely. I think most problems here are a result of scale mixups.
The problem is that for some models it works, but for some of them it catches intersections outside the collision shape.
Adding the wireframes before setting the object3D messes up with the raycaster. Not sure but I guess this is because scaling issues as well.
Here's a glitch with setting the wireframes after setObject3D
I'd start with a different approach. Create the boxes as scene children and manage their transform based on the model worldMatrix + bone offsets. It will be way easier to manage (scale up/down, reposition) and debug.
I am trying to implement a keyClick with a shift modifier but it doesn't work. Below is a basic setup of what I am trying to do. The first test_case1 can perform what I am doing but I'd like the second test_case2 to work as well but with using the Qt.ShiftModifier.
../MyTextBox.qml
Page {
id: page1
objectName: "page1"
TextField {
id: lastNameField
objectName: "lastNameField"
text: qsTr("")
}
}
tst_page.qml
import "../"
Item {
width: 800
height: 600
MyTextBox {
id: page1
}
TestCase {
id: "txtBox"
when: windowShown
function test_case1 () {
//var qmlObj = findChild(page1, "lastNameField")
var qmlObj = page1.lastNameField
// Bring to focus
mouseClick(qmlObj, Qt.LeftButton, Qt.NoModifier)
// Keypress
keyPress("Y")
keyPress("e")
keyPress("s")
tryCompare(qmlObj, "text", "Yes") // pass
}
function test_case2 () {
var qmlObj = page1.lastNameField
// Bring to focus
mouseClick(qmlObj, Qt.LeftButton, Qt.NoModifier)
// Keypress
keyClick(QT.Key_Y, Qt.ShiftModifier)
keyClick(QT.Key_E)
keyClick(QT.Key_S)
tryCompare(qmlObj, "text", "Yes") // fail
}
}
}
test output
PASS : test_case1()
FAIL! : test_case2()
Actual (): yes
Expected (): Yes
Edit: Added a simple project to github for testing.
Is there any way to list all object members/properties in QML & Qt 5.1?
Such as:
var obj=myQObject;
console.log(obj)
// expected output:
// obj { x:123..... }
This would be very helpful for debugging.
Straight javascript offers what you are looking for:
JSON.stringify(anything)
It works on QML items such as Rectangle, and it also works on most arbitrary objects!
Converting an object to a string
With meta objects you can debug all properties of any QML obj (i.e. QQuickItem).
You need some C++ to get the meta object of a QML component and get back property names and values as text in QML.
First you create a QMLDebugger class in C++ with a properties method:
QString QMLDebugger::properties(QQuickItem *item, bool linebreak)
{
const QMetaObject *meta = item->metaObject();
QHash<QString, QVariant> list;
for (int i = 0; i < meta->propertyCount(); i++)
{
QMetaProperty property = meta->property(i);
const char* name = property.name();
QVariant value = item->property(name);
list[name] = value;
}
QString out;
QHashIterator<QString, QVariant> i(list);
while (i.hasNext()) {
i.next();
if (!out.isEmpty())
{
out += ", ";
if (linebreak) out += "\n";
}
out.append(i.key());
out.append(": ");
out.append(i.value().toString());
}
return out;
}
This function can be static or instanceiable, doesn't matter. QML does not support exporting static methods from C++ to QML anyway. I use the header:
public:
Q_INVOKABLE static QString properties(QQuickItem *item, bool linebreak = true);
Now you export the class to QML. In you main.cpp add
#include "qmldebugger.h"
and
qmlRegisterType<QMLDebugger>("MyDemoLibrary", 1, 0, "QMLDebugger");
In your QML file import you new library, create an instance of QMLDebugger and start happy debugging:
import QtQuick 2.0
import MyDemoLibrary 1.0
Rectangle {
id: mainRectangle
width: 360
height: 360
color: "silver"
Text {
id: textElement
color: "#d71f1f"
text: qsTr("Hello World")
font.bold: true
font.italic: true
font.underline: true
style: Text.Raised
horizontalAlignment: Text.AlignHCenter
font.pointSize: 16
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
}
QMLDebugger {
id: qmlDebugger
}
Component.onCompleted: {
console.log("Debug mainRectangle:");
console.log(qmlDebugger.properties(mainRectangle));
console.log("Debug textElement:");
console.log(qmlDebugger.properties(textElement, false));
}
}
The full source code is available as a minimal Qt Creator project on: https://github.com/webmaster128/QMLDebugger
Just convert the QML/C++ component/object into JavaScript var object, and use the for-each syntax to list all the property:
function listProperty(item)
{
for (var p in item)
console.log(p + ": " + item[p]);
}
in your QML file, just call
onClicked:
{
listProperty(ItemID)
//or with this to list self properties
listProperty(this)
}
in case of any one wants to list only properties of an object, no signals nor slots you can use this
function listProperty(item)
{
for (var p in item)
{
if( typeof item[p] != "function" )
if(p != "objectName")
console.log(p + ":" + item[p]);
}
}
If you are not only interested in console-debugging, there is a program called GammaRay by KDAB (link) that lets you introspect and change all properties during the runtime of a QWidgets or QtQuick based program. Pretty neat!
I did not see a solution to iterate all properties yet. But maybe this helps you as a first step.
For every Quick Item you can print out the properties of Item:
import QtQuick 2.0
Rectangle {
width: 360
height: 360
function debugQuickItem(object) {
var properties = {
'activeFocus': object.activeFocus,
'activeFocusOnTab': object.activeFocusOnTab,
'anchors.alignWhenCentered': object.anchors.alignWhenCentered,
'anchors.baseline': object.anchors.baseline,
'anchors.baselineOffset': object.anchors.baselineOffset,
'anchors.bottom': object.anchors.bottom,
'anchors.bottomMargin': object.anchors.bottomMargin,
'anchors.centerIn': object.anchors.centerIn,
'anchors.fill': object.anchors.fill,
'anchors.horizontalCenter': object.anchors.horizontalCenter,
'anchors.horizontalCenterOffset': object.anchors.horizontalCenterOffset,
'anchors.left': object.anchors.left,
'anchors.leftMargin': object.anchors.leftMargin,
'anchors.margins': object.anchors.margins,
'anchors.right': object.anchors.right,
'anchors.rightMargin': object.anchors.rightMargin,
'anchors.top': object.anchors.top,
'anchors.topMargin': object.anchors.topMargin,
'anchors.verticalCenter': object.anchors.verticalCenter,
'anchors.verticalCenterOffset': object.anchors.verticalCenterOffset,
'antialiasing': object.antialiasing,
'baselineOffset': object.baselineOffset,
'children': object.children,
'childrenRect.height': object.childrenRect.height,
'childrenRect.width': object.childrenRect.width,
'childrenRect.x': object.childrenRect.x,
'childrenRect.y': object.childrenRect.y,
'clip': object.clip,
'data': object.data,
'enabled': object.enabled,
'focus': object.focus,
'height': object.height,
'implicitHeight': object.implicitHeight,
'implicitWidth': object.implicitWidth,
'layer.effect': object.layer.effect,
'layer.enabled': object.layer.enabled,
'layer.format': object.layer.format,
'layer.mipmap': object.layer.mipmap,
'layer.samplerName': object.layer.samplerName,
'layer.smooth': object.layer.smooth,
'layer.sourceRect': object.layer.sourceRect,
'layer.textureSize': object.layer.textureSize,
'layer.wrapMode': object.layer.wrapMode,
'opacity': object.opacity,
'parent': object.parent,
'resources': object.resources,
'rotation': object.rotation,
'scale': object.scale,
'smooth': object.smooth,
'state': object.state,
'states': object.states,
'transform': object.transform,
'transformOrigin': object.transformOrigin,
'transitions': object.transitions,
'visible': object.visible,
'visibleChildren': object.visibleChildren,
'width': object.width,
'x': object.x,
'y': object.y,
'z': object.z,
}
var out = "{ "
for (var key in properties)
{
out += "'" + key + "': " + properties[key] + ", "
}
out += "}"
return out;
}
Text {
id: textObject
anchors.centerIn: parent
text: "Hello World"
}
Component.onCompleted: console.log(debugQuickItem(textObject));
}
Output:
{ 'activeFocus': false, 'activeFocusOnTab': false, 'anchors.alignWhenCentered': true, 'anchors.baseline': QVariant(QQuickAnchorLine), 'anchors.baselineOffset': 0, 'anchors.bottom': QVariant(QQuickAnchorLine), 'anchors.bottomMargin': 0, 'anchors.centerIn': QQuickRectangle_QML_0(0x29857d0), 'anchors.fill': null, 'anchors.horizontalCenter': QVariant(QQuickAnchorLine), 'anchors.horizontalCenterOffset': 0, 'anchors.left': QVariant(QQuickAnchorLine), 'anchors.leftMargin': 0, 'anchors.margins': 0, 'anchors.right': QVariant(QQuickAnchorLine), 'anchors.rightMargin': 0, 'anchors.top': QVariant(QQuickAnchorLine), 'anchors.topMargin': 0, 'anchors.verticalCenter': QVariant(QQuickAnchorLine), 'anchors.verticalCenterOffset': 0, 'antialiasing': false, 'baselineOffset': 14, 'children': [object Object], 'childrenRect.height': 0, 'childrenRect.width': 0, 'childrenRect.x': 0, 'childrenRect.y': 0, 'clip': false, 'data': [object Object], 'enabled': true, 'focus': false, 'height': 17, 'implicitHeight': 17, 'implicitWidth': 80.5625, 'layer.effect': null, 'layer.enabled': false, 'layer.format': 6408, 'layer.mipmap': false, 'layer.samplerName': source, 'layer.smooth': false, 'layer.sourceRect': QRectF(0, 0, 0, 0), 'layer.textureSize': QSize(-1, -1), 'layer.wrapMode': 0, 'opacity': 1, 'parent': QQuickRectangle_QML_0(0x29857d0), 'resources': [object Object], 'rotation': 0, 'scale': 1, 'smooth': true, 'state': , 'states': [object Object], 'transform': [object Object], 'transformOrigin': 4, 'transitions': [object Object], 'visible': true, 'visibleChildren': [object Object], 'width': 80.5625, 'x': 139.71875, 'y': 171, 'z': 0, }
I'm using Aframe-React and the Aframe Click Drag component.
This is working well, and I'm now trying to work out how to add events to the entity so I can update these calculations when one of my entities is dragged (the lines are a elbow connection between them - I want to update these as the item is dragged)
The entities are given the Click Drag attributes later on, however I'm assuming its best to add the listener here.
The library has an example for events
https://github.com/jesstelford/aframe-click-drag-component/blob/master/examples/events/index.html
And registers the events as such
event-set__1="_event: dragstart; material.opacity: 0.2"
However I can't seem to work out how to make that call a function in this class,
i.e. something like
event-set__1="_event: dragstart" should call the dragStart() function.
Any clues on how to do this?
const scalar = 0.2
const offsetX = 4
const offsetY = 4.5
config.scale = {x: scalar, y: scalar, z: scalar}
if (editingHotspot.shape) {
buttonConfig.position = {
x: config.position.x + offsetX,
y: config.position.y + offsetY,
z: config.position.z,
}
const shapeTop = {
x: config.position.x,
y: config.position.y + 1.9,
z: config.position.z,
}
const buttonEdge = {
x: buttonConfig.position.x - geometry.width * 0.5 * scalar,
y: buttonConfig.position.y,
z: buttonConfig.position.z,
}
const join = {
x: shapeTop.x,
y: buttonEdge.y,
z: (shapeTop.z + buttonEdge.z) / 2,
}
lineX = {
lineWidth: 10,
path: AFRAME.utils.coordinates.stringify(buttonEdge) + ',' + AFRAME.utils.coordinates.stringify(join),
color: '#fff',
}
lineY = {
lineWidth: 10,
path: AFRAME.utils.coordinates.stringify(shapeTop) + ',' + AFRAME.utils.coordinates.stringify(join),
color: '#fff',
}
}
}
let dragStart = (e) => {
console.log(e)
}
let params = {
'hotspots-button': 'text:' + (button.label != null ? button.label : '') + ';' + 'icon:' + (button.icon != null ? button.icon.preview : '') + ';',
draw: 'width:256; height:' + (button.icon != null ? '256' : '128') + ';',
}
return (
<Entity className='hotspot button' {...params} >
<Entity
className='hotspot button'
primitive='a-image'
look-at='[camera]'
{...{geometry}}
scale={config.scale}
position={editingHotspot.shape ? buttonConfig.position : config.position}
/>
{
editingHotspot.shape &&
<Entity>
<Shape config={config} editingHotspot={editingHotspot}/>
<Entity meshline={lineX}/>
<Entity meshline={lineY}/>
</Entity>
}
</Entity>
)
As far as i see, Kevin's event-set component sets the target / self attributes (line 121 of his non-minified dist), which means it can't call methods ( except for update, which is called whenever an attribute is changed)
I'd make my own listener, either in my component, or just a listener -> caller
AFRAME.registerComponent("listener", {
init: function() {
this.el.addEventListener("startDrag", (e)=> {
// call your function here.
// if you want to call another component's function, you can do
// this.el.components.componentWithMethod.method()
}
}
}
Something like this.
Is there a way to hide the language selection key from the virtual keyboard without use a custom layout?
I was able to hide the language key with a workaround:
property var keyboardLayout: inputPanel.keyboard.layout
function findChildByProperty(parent, propertyName, propertyValue, compareCb) {
var obj = null
if (parent === null)
return null
var children = parent.children
for (var i = 0; i < children.length; i++) {
obj = children[i]
if (obj.hasOwnProperty(propertyName)) {
if (compareCb !== null) {
if (compareCb(obj[propertyName], propertyValue))
break
} else if (obj[propertyName] === propertyValue) {
break
}
}
obj = findChildByProperty(obj, propertyName, propertyValue, compareCb)
if (obj)
break
}
return obj
}
onKeyboardLayoutChanged: {
if(keyboardLayout!=""){
var ChangeLanguageKey= findChildByProperty(inputPanel.keyboard, "objectName", "changeLanguageKey", null)
if(ChangeLanguageKey){
ChangeLanguageKey.visible=false
}
}
}
InputPanel {
id: inputPanel
z: 99
y: parent.height
anchors.left: parent.left
anchors.right: parent.right
states: State {
name: "visible"
when: inputPanel.active
PropertyChanges {
target: inputPanel
y: parent.height - inputPanel.height
}
}
transitions: Transition {
from: ""
to: "visible"
reversible: true
ParallelAnimation {
NumberAnimation {
properties: "y"
duration: 400
easing.type: Easing.InOutBack
}
}
}
CustomComponents.AutoScroller {
id:autoscroller
panelY: inputPanel.y
}
}
This only works in version 5.9 where the objectname property is defined with "changeLanguageKey", for previous versions set the property in the source code and recompile.
No, not without using a custom layout.
You can always modify the layouts that come with the keyboard though.
I was able to hide the hideKeyboard key with this trick. I basically tried to get the reference of the emoji key and thereby was able to disable the next key which is hideKeyboard key.
function disableKey(parent, objectText)
{
var obj = null
if (parent === null)
return null
var children = parent.children
for (var i = 0; i < children.length; i++) {
obj = children[i]
if (obj.text === objectText && obj.toString().substring(0, 7) === "BaseKey") {
console.log("Disabling symbols. " + obj.text)
obj.enabled = false
}
else if(obj.displayText === "HWR"){
console.log("Disabling Handwriting mode button." + obj.displayText + " " + objectText)
obj.visible = false
}
else if(obj.text === ":-)" && obj.toString().substring(0, 7) === "BaseKey"){
console.log("Disabling hidekeyboard key." + obj.text)
children[i+1].visible = false
}
obj = disableKey(obj, objectText)
if (obj)
break
}
return obj
}