I'm new to Qt3d and I need to handle user input on scene with multiple models.
In particular i need to find with model the user has clicked.
I try with mouseHandler and ObjectPicker but it seems not to work. Does someone have an example?
For example, if I have:
Entity {
Camera { id: camera ... }
FirstPersonCameraController {
camera: camera
}
components: [
RenderSettings{
activeFrameGraph: ForwardRenderer {
camera: camera
clearColor: "transparent"
},
InputSettings{}
]
MouseDevice {
id: mouse1
sensitivity: 0.1
}
SphereMesh {
id: sphereMesh
radius: 3
}
PhongMaterial{
id: material
}
Entity {
id: sphere1
components: [sphereMesh, material]
MouseHandler {
sourceDevice: mouse1
onClicked: console.log("[sphere 1] clicked"
}
}
Entity {
id: sphere2
components: [sphereMesh, material]
MouseHandler {
sourceDevice: mouse1
onClicked: console.log("[sphere 2] clicked"
}
}
}
I need to distinguish if the user clicks on sphere1 or sphere2, but if I click on the sphere I cannot see any log!
You need to create an ObjectPicker and attach it as a component to each entity. You can delete the MouseHandler stuff.
Entity {
id: sphere2
components: [sphereMesh, material, spherePicker]
}
ObjectPicker{
id: spherePicker
onPressed:{
console.log("Sphere clicked")
}
}
Note that by default, this will do bounding box raycasting, so it is very possible that you click somewhere near the mesh but not exactly on it, and it will register a click. If you want to do triangle picking, you can specify that by changing the rootEntity's pickingSettings component, which would solve this if that is a problem for you. I'm assuming this would be much slower than bounding box raycasting, but with big, 100 mb .stl files I didn't notice any visible slowdown.
components: [
RenderSettings{
activeFrameGraph:ForwardRenderer {
camera: camera
clearColor: "transparent"
},
InputSettings{}
pickingSettings.pickMethod: PickingSettings.TrianglePicking
pickingSettings.faceOrientationPickingMode: PickingSettings.FrontAndBackFace
]
If you want to know where in worldspace the object is pressed, most of the ObjectPicker methods have a PickEvent that you can look at.
ObjectPicker{
onPressed:{
console.log("Pressed at: " + pick.worldIntersection)
//If using triangle picking, you can also see index of the pressed triangle
console.log("Triangle index: " + pick.triangleIndex)
}
}
Another thing to note: You'll see that for ObjectPicker, I am using onPressed rather than onClicked. For large meshes (say 90mb .stl files), onClicked was unpredictable. I'd click the mesh and sometimes it would fire, sometimes it wouldn't. But onPressed would always fire. This was my observation with Qt 5.8 and Qt 5.9.
Relevant documentation is here
Related
This example is pretty contrived, but it illustrates the behavior. I know you can use .accessibilityIdentifier to uniquely identify a control, but I'm just trying to better understand the interplay between XCUIElement and XCUIElementQuery.
Let's say you have an app like this:
import SwiftUI
struct ContentView: View {
#State var showRedButton = true
var body: some View {
VStack {
if showRedButton {
Button("Click me") {
showRedButton = false
}
.background(.red)
}
else {
HStack {
Button("Click me") {
showRedButton = true
}
.background(.blue)
Spacer()
}
}
}
}
}
And you are UI testing like this:
import XCTest
final class MyAppUITests: XCTestCase {
func testExample() throws {
let app = XCUIApplication()
app.launch()
print(app.debugDescription)
// At this point, the Element subtree shows a single Button:
// Button, 0x14e40d290, {{162.3, 418.3}, {65.3, 20.3}}, label: 'Click me'
let btn = app.buttons["Click me"]
btn.tap() // <-- This tap makes the red button disappear and shows the blue button
print(app.debugDescription)
// Now, the Element subtree shows a single Button that has a different ID
// and different x-y coordinates:
// Button, 0x15dc12e50, {{0.0, 418.3}, {65.3, 20.3}}, label: 'Click me'
btn.tap() // <-- This tap now works on the blue button?? Without requerying?
print(app.debugDescription)
// The red button reappears, but with a different ID (which makes sense).
}
}
Why does the second tap work, even though it's a different control? This must mean that SwiftUI is automatically re-running the XCUIElementQuery to find the button that matches "Click me". Apparently the variable btn isn't linked to the control with the ID 0x14e40d290. Does this mean XCUIElement actually represents an XCUIElementQuery? I expected it to require me to explicitly re-run the query like this,
btn = app.buttons["Click me"]
prior to running the 2nd tap, or the tap would've said that btn was no longer available.
The final print of the Element subtree shows that the red button has a different ID now. This makes sense, because when SwiftUI redraws the red button, it's not the same instance as the last time. This is explained well in the WWDC videos. Nevertheless, at the moment I connected the variable "btn" to the control, I thought there was a tighter affiliation. Maybe UI testing has to behave this way because SwiftUI redraws controls so frequently?
my question is about using a QML DragHandler to move a QML Item. I have successfully implemented position through dragging (when holding the Ctrl modifier) like so:
DragHandler {
dragThreshold: 0
acceptedModifiers: Qt.ControlModifier
}
Now I would like to add another handler that allows me to precisely position the element. Other software does this throught the use of the shift modifier. So what I want to do is move the element not by the pixel amount that the mouse moves, but an amount smaller than that. Ideally I would want to do something like this:
DragHandler {
dragThreshold: 0
acceptedModifiers: Qt.ShiftModifier
onActiveTranslationChanged: {
activeTranslation *= 0.5;
}
}
Unfortunatelly activeTranslation is read-only and I don't see any other property I could use and I can't think of any other way to do it... Does anybody have an idea?
Thank you very much in advance!
Unfortunately Qt doesn't provide any way to change the drag speed AFAIK.
But this is a way to achieve it:
Rectangle
{
id: theDraggableElement
width: 100
height: width
color: "red"
DragHandler
{
id: dragHandlerFast
dragThreshold: 0
acceptedModifiers: Qt.ControlModifier
target: theDraggableElement
}
}
Item
{
id: invisibleItemForSlowDragging
width: theDraggableElement.width
height: theDraggableElement.height
Binding { restoreMode: Binding.RestoreBinding; when: !dragHandlerSlow.active; target: invisibleItemForSlowDragging; property: "x"; value: theDraggableElement.x }
Binding { restoreMode: Binding.RestoreBinding; when: !dragHandlerSlow.active; target: invisibleItemForSlowDragging; property: "y"; value: theDraggableElement.y }
DragHandler
{
id: dragHandlerSlow
dragThreshold: 0
acceptedModifiers: Qt.ShiftModifier
target: invisibleItemForSlowDragging
onTranslationChanged:
{
theDraggableElement.x = invisibleItemForSlowDragging.x - dragHandlerSlow.translation.x / 2
theDraggableElement.y = invisibleItemForSlowDragging.y - dragHandlerSlow.translation.y / 2
}
}
}
I have tested this with Qt 5.15.2.
I'm new to Qt, and from what I've read on qt-project.org and other places; QtQuick seems like an attractive option because of its ability to work on both pointer and touch based devices. My problem is getting it to work well with c++.
I decided to write a variant of Conway's Game of Life as a next step after "Hello World". I am thoroughly mystified as to how to get the "board" -- a [height][width][bytes-per-pixel] array of char -- integrated into the scene graph.
Basically, the process is that the "LifeBoard" iterates through its rules and updates the char*/image. I've got this simple QML:
:::QML
ApplicationWindow {
id: life_app_window
visible: true
title: qsTr("Life")
menuBar: MenuBar {
Menu {
title: qsTr("File")
MenuItem {
text: qsTr("Quit")
onTriggered: Qt.quit();
}
}
}
toolBar: ToolBar {
id: lifeToolBar;
ToolButton {
id: toolButtonQuit
text: qsTr("Quit")
onClicked: Qt.quit()
}
ToolButton {
id: toolButtonStop
text: qsTr("Stop")
enabled: false
//onClicked:
}
ToolButton {
id: toolButtonStart
text: qsTr("Start")
enabled: true
//onClicked: //Start life.
}
ToolButton {
id: toolButtonReset
text: qsTr("Stop")
// onClicked: //Reset life.
}
}
Flow {
id: flow1
anchors.fill: parent
//*****
// WHAT GOES HERE
//*****
}
statusBar: StatusBar {
enabled: false
Text {
// Get me from number of iterations
text: qsTr("Iterations.")
}
}
}
I want to image to come from a class with a api kinda like this:
class Life {
public:
QImage getImage() {}
// Or
char* getPixels(int h, int w, QImage::Format_ARGB8888) {}
}
I have no clue, and hours wading through tutorials did not help. How does one link a char* image in c++ to a ??? in QML so that the QML can start/stop the "Life" loop and so that the "Life" loop and update the char array and notify QML to redraw it?
Note: I've looked at subclassing QQuickImageProvider based on the info here. The problem with this approach is that I cannot see how to let c++ "drive" the on screen image. I wish to pass control from QML to c++ and let c++ tell QML when to update the display with the changed image. Is there a solution with this approach? Or another approach entirely.
First way to do that would be creating a Rectangle for each game pixel in QML, which might be fancy for a 8x8 board, but not for a 100x100 board, since you need to write the QML code manually for each pixel.
Thus I'd go for images created in C++ and exposed to QML. You call them via an image provider to allow asynchronous loading. Let Life do the logic only.
The image is called from QML like this:
Image {
id: board
source: "image://gameoflife/board"
height: 400
width: 400
}
Now gameoflife is the name of the image provider and board the so-called id you can use later.
Register gameoflife in you main.cpp
LifeImageProvider *lifeIP = new LifeImageProvider(life);
engine.addImageProvider("gameoflife", lifeIP);
where engine is your main QQmlApplicationEngine and life an instance of your Life game engine.
LifeImageProvider is your class to create pixeldata. Starts somehow like
class LifeImageProvider : public QQuickImageProvider
{
public:
LifeImageProvider(Life *myLifeEngine);
QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize);
private:
Life *myLifeEngine_;
};
The important method is requestPixmap, which is called from QML. You need to implement it.
To refresh the game board when Life sends a stateChanged() signal, expose life as a global object to QML:
context->setContextProperty("life", &life);
You can bind the signal to QML
Image {
id: board
source: "image://gameoflife/board"
height: 400
width: 400
}
Connections {
target: life
onStateChanged: {
board.source = "image://gameoflife/board?" + Math.random()
// change URL to refresh image. Add random URL part to avoid caching
}
}
Just for fun, and at the risk of downvotes for a completely tangential answer, here's a GameOfLife implemented entirely in QML, just put it in a .qml file and run it with qmlscene. Works on Qt 5.3.0, and runs surprisingly (to me) fast on an old Core 2 Duo lappy. I'm sure it'll never be as fast/efficient as a C++ QQuickImageProvider based solution though, but it does make the point it's possible to do quite a lot in QML without resorting to C++.
import QtQuick 2.2
Rectangle {
id: main
width: 640
height: 640
color: '#000088'
Timer {
interval: 1000/60
running: true
repeat: true
onTriggered: {advance();display();}
}
Component {
id: cellComponent
Rectangle {
objectName: 'cell'
property int row: 0
property int col: 0
x: main.width/2+width*col
y: main.height/2+height*row
width: 5
height: 5
radius: 2
smooth: true
color: '#ffcc00'
}
}
property var cells: null
Component.onCompleted: {
cells=[[-1, 0],[-1, 1],[ 0,-1],[ 0, 0],[ 1, 0]];
display();
}
function display() {
// Just completely regenerate display field each frame
// TODO: might be nicer to do differential updates, would allow birth/death animations
// Nuke all previously displayed cells
for (var i=0;i<children.length;i++) {
if (children[i].objectName=='cell') {
children[i].destroy();
}
}
// Show current set of cells
for (var i=0;i<cells.length;i++) {
var c=cellComponent.createObject(
main,
{'row':cells[i][0],'col':cells[i][1]}
);
}
}
function advance() {
// Build a hash of the currently alive cells and a neighbour count (includes self)
var a=new Object;
var n=new Object;
for (var i=0;i<cells.length;i++) {
var p=cells[i]
var r=p[0];
var c=p[1];
if (!(r in a)) a[r]=new Object;
a[r][c]=1;
for (var dr=r-1;dr<=r+1;dr++) {
for (var dc=c-1;dc<=c+1;dc++) {
if (!(dr in n)) n[dr]=new Object;
if (!(dc in n[dr])) n[dr][dc]=0;
n[dr][dc]+=1;
}
}
}
// For all live cells, assess viability
var kill=[];
var stay=[];
for (var r in a) {
for (var c in a[r]) {
if (n[r][c]-1<2 || n[r][c]-1>3)
kill.push([Number(r),Number(c)]);
else
stay.push([Number(r),Number(c)]);
}
}
// For neighbours of live cells, assess potential for births
var born=[];
for (var r in n) {
for (var c in n[r]) {
if (!((r in a) && (c in a[r]))) {
if (n[r][c]==3)
born.push([Number(r),Number(c)]);
}
}
}
cells=stay.concat(born)
}
}
And for a pure QML version using GLSL (via a recursive QML ShaderEffect) to compute the Game of Life rules on GPU see here.
I'm trying to find a way to do a transition on a QML element, when a binding changes. Say you have a Text element, with the text property bound to something. What I want is when the data in the binding changes, the element fades out (Still displaying old data), switches and fades back in with the new data (the actual transition occurring while the element isn't visible.)
I've been searching everywhere for a way to do this but I can figure it out. I've tried using Qt Quick animations within QML, but the data itself changes before the animation runs, leaving the animation unnecessary. I've tried creating a custom QDeclarativeItem object that calls an animation within the QDeclarativeItem::paint() but I can't figure out how to get it to actually run.
I should note here that I know my bindings are working fine as the displayed data changes, I just can't get these animations to run at the proper time.
Here is what I tried with QML:
Text {
id: focusText
text: somedata
Behavior on text {
SequentialAnimation {
NumberAnimation { target: focusText; property: "opacity"; to: 0; duration: 500 }
NumberAnimation { target: focusText; property: "opacity"; to: 1; duration: 500 }
}
}
}
And here is what I tried in implementing a custom QDeclarativeItem:
// PAINTER
void AnimatedBinding::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
// Setup the pen
QPen pen(m_color, 2);
painter->setPen(pen);
painter->setOpacity(this->opacity());
// Draw the item
if (m_bindingType == QString("text")) {
QPropertyAnimation animation(this, "opacity");
animation.setDuration(1000);
animation.setStartValue(1);
if (drawn) {
animation.setStartValue(1);
animation.setEndValue(0);
animation.start();
} else drawn = true;
painter->drawText(boundingRect(), m_data.toString());
animation.setEndValue(0);
animation.start();
} else {
qCritical() << "Error unknown binding type!";
return;
}
}
But like I said, the animation that I start within the painter never actually fires.
Any tips? Anyone ever done this before? I've been banging my head on this for about a week.
How about doing it in qml only this ways :
Define a custom element of your own type, that behaves the way you want it to.
Use this element instead of traditional element to be animated.
eg. I have create a custom 'AnimatedText' type to have the fading in and fading out behavior on the text elements whenever text related to them changes.
File 1 : AnimatedText.qml
import QtQuick 1.0
Item
{
id: topParent
property string aText: ""
property string aTextColor: "black"
property int aTextFontSize: 10
property int aTextAnimationTime : 1000
Behavior on opacity { NumberAnimation { duration: aTextAnimationTime } }
onATextChanged:
{
topParent.opacity = 0
junkTimer.running = true
}
Timer
{
id: junkTimer
running: false
repeat: false
interval: aTextAnimationTime
onTriggered:
{
junkText.text = aText
topParent.opacity = 1
}
}
Text
{
id: junkText
anchors.centerIn: parent
text: ""
font.pixelSize: aTextFontSize
color: aTextColor
}
}
and in your main.qml
import QtQuick 1.0
Rectangle
{
id: topParent
width: 360
height: 360
AnimatedText
{
id: someText
anchors.centerIn: parent
aText: "Click Me to change!!!.."
aTextFontSize: 25
aTextColor: "green"
aTextAnimationTime: 500
}
MouseArea
{
anchors.fill: parent
onClicked:
{
someText.aText = "Some random junk"
}
}
}
i want to add some ui inside camera view blackberry 10
like photobomber samples on github
https://github.com/blackberry/Cascades-Samples/tree/master/photobomber
but i want to overlay the image while the camera is active and save the photo + image inside the photo to memory
can somebody tell me how to do that ?
best regards,
adit
You should opt for DockLayout whenever you want to overlap any controls. Go through the following code, you should get the idea
Page {
content: Container {
gestureHandlers: [
TapHandler {
onTapped: cameraControl.capturePhoto()
}
]
layout: DockLayout {
}
Camera {
id: cameraControl
onCameraOpened: {
cameraControl.startViewfinder();
}
}
Button {
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
text: "Overlapping button"
}
}
onCreationCompleted: {
if (cameraControl.allCamerasAccessible) {
cameraControl.open(CameraUnit.Rear);
}
}
}
To capture photo you can use capturePhoto method of camera control. Go through the documentation to find more methods.
Do note that Camera control should be declared at the top in the container & other controls should be declared below it to overlap controls over it.
Don't forget to provide Camera acess permission in bar-descriptor, to add LIBS += -lcamapi in pro file & to import bb.cascades.multimedia 1.0 in qml.