How to define Widget at runtime time and what is possible?
For example, the following problem:
required is a button e.g. 'button-1' - widget to start a sequence.
Now user want to create an 3D-Label that becomes visible after click the 'button-1'
and now for each sequence and step a the 3D-Label have to be visible as long as the sequence is played. The 3d label should display an information for the specific step and also should to have specific position.
Unfortunately, it is not possible (using the supported functionality) to create widget on the fly - means create a new feature on run time where the widget was not defined in the design time. Theoretical with some more intensive work – it will be possible – we simple need to check what the UI is doing when you copy and paste widget (but it is not easy ☹- and it is not sure if this solution will be stable(if you will be able to implement 1:1 UI) and if it will work in later version -because PTC dev team could change some functionality – but will not change your code. For the most tasks it enough to use few 3d widgets (3Dlabels) the most are invisible and then switch the visibility on runtime and move them on the desired location. According request on address of this issue , may be it is worth here to mention following statements of the PTC dev :
.) Question: “Vuforia Studio 8.5.3.Is it possible to create 2D and 3D widgets from Java Script?
Answer: If you mean to dynamically create/instatiate a widget and set its properties at runtime, no we have no such capability today.
The simplest workaround is to pre-generate the widgets and manage their visibility at runtime.
2.) Question: Is it possible to create 2D widget dynamically by Java Script?
Answer: Its fairly easy to have a couple of hidden widgets that are displayed and moved with a tap event.
It might be possible to create some 2d widgets on the fly, but 3d widgets would be a bit harder as more of the unsupported api under the hood would be needed.
According to the statement of the PTC development team - here the we will consider the possible workaround :.
The following requirement:
When the sequence 1 is played the labels 1, 2 and 3 have to be visible.
When the sequence 2 is played the labels 4, 5 and 6 have to be visible.
In all of these labels are only one word in it, so not a step description or something like this.
Here is a sample table with coordinates for a better overview:
There is no a real reason to have 4,5,6 here. In this case we can use 1,2,3 again - also for step 2 but we could here change the position and will change the text content. In this case we need to have to define number of 3DLabel widgets which is equal to the maximum of used notes per step - and display and update only the necessary number of widget in particular step.
To demonstrate the workaround the following example was created and tested. This example should show the suggestion above. It is not perfect but should demonstrate the general approach to achieve similar goal.
The table with values is a json file- in the attached example is the file steps.json in the Project upload folder. Here example of values:
{"1":{"3DLabel-1":{"visible":true,"text":"text Label1 Step 1", "x":0.0,"y":0.1,"z":0.0,"rx":0.0,"ry":0.0,"rz":0.0,"scale":1.0,"class":"ptc-3DLabel1"},
"3DLabel-2":{"visible":true,"text":"text Label2 Step 1", "x":0.0,"y":0.2,"z":0.0,"rx":0.0,"ry":0.0,"rz":0.0,"scale":1.0,"class":"ptc-3DLabel2"},
"3DLabel-3":{"visible":true,"text":"text Label3 Step 1", "x":0.0,"y":0.3,"z":0.0,"rx":0.0,"ry":0.0,"rz":0.0,"scale":1.0,"class":"ptc-3DLabel3"}},
"2":{"3DLabel-1":{"visible":true,"text":"text Label1 Step 2", "x":0.0,"y":0.1,"z":0.1,"rx":0.0,"ry":10.0,"rz":0.0,"scale":1.0,"class":"ptc-3DLabel2"},
"3DLabel-2":{"visible":false,"text":"text Label2 Step 2", "x":0.0,"y":0.2,"z":0.0,"rx":0.0,"ry":10.0,"rz":0.0,"scale":1.0,"class":"ptc-3DLabel2"},
"3DLabel-3":{"visible":true,"text":"text Label3 Step 2", "x":0.0,"y":0.3,"z":0.1,"rx":0.0,"ry":10.0,"rz":0.0,"scale":1.0,"class":"ptc-3DLabel2"}},
"3":{"3DLabel-1":{"visible":true,"text":"text Label1 Step 3", "x":0.0,"y":0.1,"z":0.0,"rx":0.0,"ry":20.0,"rz":0.0,"scale":1.0,"class":"ptc-3DLabel3"},
....
"8":{"3DLabel-1":{"visible":true,"text":"text Label1 Step 8", "x":0.0,"y":0.1,"z":0.0,"rx":0.0,"ry":0.0,"rz":0.0,"scale":1.0,"class":"ptc-3DLabel1"},
"3DLabel-2":{"visible":true,"text":"text Label2 Step 8", "x":0.0,"y":0.2,"z":0.0,"rx":0.0,"ry":0.0,"rz":0.0,"scale":1.0,"class":"ptc-3DLabel2"},
"3DLabel-3":{"visible":true,"text":"text Label3 Step 8", "x":0.0,"y":0.3,"z":0.0,"rx":0.0,"ry":0.0,"rz":0.0,"scale":1.0,"class":"ptc-3DLabel3"}}}
The table contains the most possible values to set for 3DLabels. We can add other or omit values in the list. In generally we need only the values which should be changed but such list template which contains all values is better for editing. It should still work so far, the json syntax is correct
First point is to load the list to a global variable from the project upload folder. Here a sample code:
$scope.jsonData_steps={}; //global $scope variable
//==================================
readSteps=function (jsonFile){
console.warn("**=>readSteps :: "+jsonFile);
fetch(jsonFile)
.then(response=>response.text())
.then(data=>{$scope.jsonData_steps=JSON.parse(data);
console.warn( JSON.stringify($scope.jsonData_steps))})
.catch((wrong) => {console.log("problem in fetch: "); console.warn(wrong)})
}
//==================================
$scope.Init=function() {
$timeout(readSteps('app/resources/Uploaded/'+stepsjson),200);
}
//=================================================================================================
// $ionicView.afterEnter -> this event fires when 2d view was entered
//=================================================================================================
$scope.$on('$ionicView.afterEnter',function(){
console.log("$ionicView.afterEnter was called");
$scope.Init();}) //event: when 2d View loaded
//=================================================================================================
further in the "stepstarted" event the code will set the values of the widget properties which are contained by the json object with the same number as the started step number:
//=================================================================================================
$scope.$on('stepstarted', function(evt, arg1, arg2, arg3) {
var parsedArg3 = JSON.parse(arg3);
console.log("stepstarted stepNumber="+parsedArg3.stepNumber + " nextStep="+parsedArg3.nextStep);
$timeout(()=>{ $scope.setMutipleProps($scope.jsonData_steps[parsedArg3.stepNumber.toString()])},10)
$scope.setWidgetProp('label-1', 'text',"STEP: "+ parsedArg3.stepNumber)
$scope.$applyAsync();
});
//=================================================================================================
//this function set multiply properties from a list
//=================================================================================================
$scope.setMutipleProps = function(obj){
Object.keys(obj).forEach(function (item) {
for(var prop in obj[item]) {
$scope.view.wdg[item][prop]=obj[item][prop];
//print the setting for debugging
console.log("==>$scope.view.wdg["+item+"]["+prop+"]="+obj[item][prop] )
}
})
$scope.$applyAsync(); };
///=================================================================================================
Finally, the project was tested and it was working as expected:
The demo project is attached to this article.
View full tip