I am using a QQuickWindow, QQuickRenderControl, and a QOffscreenSurface to render a Qml scene into an OpenGL framebuffer object.For this reason, I am using qt example Qt/Examples/Qt-5.15.2/quick/rendercontrol as a base with some minor modifications.
Since no window is actually created, it becomes necessary to artificially transfer mouse and keyboard events to QQuickWindow.There was already mousePressEvent example for mouseEvents on renderControl example which works fine that means I can send mouse events to qml.
Similarly, I tried to implement keyPressEvent and keyReleaseEvent by using QCoreApplication:: sendEvent (m_quickWindow, e); function call but it does not send key events to qml even if I set force to true for a rectangle. When I debug the issue, I have observed that activeFocusItem is NULL on qquickwindow.cpp ==> deliverKeyEvent function.
After that, I put a mousearea inside rectangle and call forceActiveFocus() function for rectangle and then keyPressEvent are sent to qml successfully.Also, If I put timer and call forceActiveFocus for rectangle on some interval it also works but If I try to call same forceActiveFocus() function on Component.onCompleted it is not sending any key events to qml.
So Do I have to call forceActiveFocus() to manage key sending for offscreen surface? Also if I have to call forceActiveFocus() from qml, then is there any way to call it instead of timer or mousearea?
Following is my code snippets:
import QtQuick 2.5
import QtQuick.Window 2.2
Rectangle {
visible: true
width: 1280
height: 480
color: "#000"
Rectangle
{
id:keyPressRect
width:100
height: 100
//focus: true
color: activeFocus? "red": "blue"
onFocusChanged: console.log("focus changed to: " + focus)
onActiveFocusChanged: console.log("active focus changed to: " +activeFocus)
Component.onCompleted:
{
console.log("onCompleted(), focus: " + focus + " activeFocus: " + activeFocus)
forceActiveFocus()
console.log("onCompleted(), focus: " + focus + " activeFocus: " + activeFocus)
}
/*Component.onCompleted: mTimer.start()
Timer {
id:mTimer
interval: 5000
repeat: false
onTriggered: {
keyPressRect.forceActiveFocus()
console.log("testtt")
}
}*/
Keys.onPressed:
{
console.log("key pressed on qml")
}
Keys.onReleased:
{
console.log("key released on qml")
}
MouseArea
{
anchors.fill: parent
onClicked:
{
parent.forceActiveFocus()
console.log("mouse clicked")
}
}
}
}
And sending keyPressEvent from C++ as the following:
void WindowSingleThreaded::keyPressEvent(QKeyEvent *e)
{
qDebug() << "key press event from cpp";
qDebug() << e->key() << "key is";
QCoreApplication::sendEvent(m_quickWindow, e);
}
Related
I am creating a game for touch screens that requires 2-4 players to each have access to a pair of slider controls. The problem is that the QML Slider control responds to touch as a mouse event and seizes the focus. Then only one player can access a single control at a time. I need multiple sliders to respond to touch events simultaneously. My question is how to do that?
With the help of a variety of stack overflow posts, I have been able to create my own answer that so far seems to work. I detail the answer below in the answer section to save other newbies like me the trouble.
There is a pure qml solution to this problem. The TouchSlider C++ object in my first answer (elsewhere in this thread) was unnecessary. Here I have modified the code to the TouchSlider qml code to eliminate references to touchslider (the TouchSlider C++ object).
TouchPoint.qml:
import QtQuick 2.0
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.2
Item {
property string sliderTitle
property real sliderMin
property real sliderMax
property real sliderVal
ColumnLayout{
id: column1
Label {
text: qsTr(sliderTitle)
font.pointSize: 10
}
Slider {
id: touchSlider1
minimumValue: sliderMin
maximumValue: sliderMax
orientation: Qt.Vertical
value: sliderVal
onValueChanged: function(){
sliderVal = Math.round(this.value);
labelSliderValue.text = qsTr(JSON.stringify(sliderVal));
}
}
Label {
id: labelSliderValue
text: qsTr(JSON.stringify(sliderVal))
font.pointSize: 10
}
function sliderSetValueFromTouch(position){
// Assume qs a vertical slider
var minvalue = touchSlider1.minimumValue;
var maxvalue = touchSlider1.maximumValue;
// Since this is a vertical slider, by assumption, get the height
var height = touchSlider1.height;
// Compute the new value based on position coordinate
var newvalue = (height-position)/height * (maxvalue-minvalue);
if (newvalue<minvalue) newvalue = minvalue;
if (newvalue>maxvalue) newvalue = maxvalue;
//qDebug() << newvalue;
// Set the value of the slider
touchSlider1.value = newvalue;
}
MultiPointTouchArea{
anchors.fill: touchSlider1
touchPoints: [
TouchPoint {
id: point1
onPressedChanged: function(){
if(pressed){
//console.log("pressed");
//console.log(touchslider.testStringReturn());
//touchslider.sliderSetValueFromTouch(touchSlider1,point1.y);
column1.sliderSetValueFromTouch(point1.y);
}
}
}
]
onTouchUpdated: function(){
//touchslider.sliderSetValueFromTouch(touchSlider1,point1.y);
column1.sliderSetValueFromTouch(point1.y);
}
}
}
}
The touchslider.h and touchslider.cpp files add no value.
I could not find a pure QML way to solve the problem but I wanted to minimize the use of C++. Using C++, I create an object TouchSlider and add it to my qml scene. The TouchSlider object has a simple function to update the value of a vertical slider according to a position argument. Then in the QML code, I add a MultiPointTouchArea on top of a regular slider and respond to the touch events by calling C++ function.
Here are all my files for a project called SliderPair.
SliderPair.pro:
QT += qml quick widgets
QT += quickcontrols2
QT += core
CONFIG += c++11
SOURCES += main.cpp \
touchslider.cpp
RESOURCES += \
qml.qrc
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH += qml
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
HEADERS += \
touchslider.h
DISTFILES +=
main.cpp:
#include <QApplication>
#include <QQmlApplicationEngine>
// add following includes for exposing new class TouchSlider to QML
#include <QQmlEngine>
#include <QQmlContext>
#include "touchslider.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
//Create an object of type TouchSlider
//When a scoped pointer goes out of scope the object is deleted from memory. Good housekeeping:
QScopedPointer<TouchSlider> touchslider (new TouchSlider);
QQmlApplicationEngine engine;
engine.addImportPath(QStringLiteral("qml"));
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
//QML can now refer to the TouchSlider object using the handle "touchslider":
engine.rootContext()->setContextProperty("touchslider",touchslider.data());
return app.exec();
}
touchslider.h:
#ifndef TOUCHSLIDER_H
#define TOUCHSLIDER_H
#include <QObject>
#include <QDebug>
#include <QtQuickControls2>
class TouchSlider : public QObject
{
Q_OBJECT
public:
explicit TouchSlider(QObject *parent = 0);
//call Q_INVOKABLE macro to set up functions for QML
Q_INVOKABLE void testDebug(); //hello world from C++
Q_INVOKABLE QString testStringReturn(); //hello world to javascript
Q_INVOKABLE void sliderSetValueFromTouch(QQuickItem *qs,int position );//use touch event to set slider value
signals:
public slots:
};
#endif // TOUCHSLIDER_H
touchslider.cpp:
#include "touchslider.h"
TouchSlider::TouchSlider(QObject *parent) : QObject(parent)
{
}
void TouchSlider::testDebug()
{
qDebug() << "Hello from C++";
}
QString TouchSlider::testStringReturn()
{
QString message = "Hi from C++";
return message;
}
void TouchSlider::sliderSetValueFromTouch(QQuickItem *qs, int position)
{
// Assume qs a vertical slider
// Get its properties (its slider properties are accessible even though it is declared as QQuickItem)
// minimumValue and maximumValue are of type QVariant so we need to cast them as double
double minvalue = qs->property("minimumValue").value<double>();
double maxvalue = qs->property("maximumValue").value<double>();
// Since this is a vertical slider, by assumption, get the height
double height = qs->property("height").value<double>();
// Compute the new value based on position coordinate
double newvalue = (height-position)/height * (maxvalue-minvalue);
if (newvalue<minvalue) newvalue = minvalue;
if (newvalue>maxvalue) newvalue = maxvalue;
//qDebug() << newvalue;
// Set the value of the slider
qs->setProperty("value",newvalue);
}
TouchSlider.qml:
import QtQuick 2.0
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.2
Item {
property string sliderTitle
property real sliderMin
property real sliderMax
property real sliderVal
ColumnLayout{
Label {
text: qsTr(sliderTitle)
font.pointSize: 10
}
Slider {
id: touchSlider1
minimumValue: sliderMin
maximumValue: sliderMax
orientation: Qt.Vertical
value: sliderVal
onValueChanged: function(){
sliderVal = Math.round(this.value);
labelSliderValue.text = qsTr(JSON.stringify(sliderVal));
}
}
Label {
id: labelSliderValue
text: qsTr(JSON.stringify(sliderVal))
font.pointSize: 10
}
MultiPointTouchArea{
anchors.fill: touchSlider1
touchPoints: [
TouchPoint {
id: point1
onPressedChanged: function(){
if(pressed){
//console.log("pressed");
//console.log(touchslider.testStringReturn());
touchslider.sliderSetValueFromTouch(touchSlider1,point1.y);
}
}
}
]
onTouchUpdated: function(){
touchslider.sliderSetValueFromTouch(touchSlider1,point1.y);
}
}
}
}
PlayerControls.qml:
import QtQuick 2.0
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.2
Item {
// These properties act as constants, useable outside this QML file
property string playerName
property real priceMin
property real priceMax
property real qualityMin
property real qualityMax
property real priceValue
property real qualityValue
property int sliderWidth
ColumnLayout{
id: columnLayout1
width: 640
height: 480
Layout.minimumWidth: 640
Layout.fillWidth: true
anchors.fill: parent
spacing: 10.2
Label {
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
id: labelPlayer1
text: qsTr(playerName)
font.pointSize: 10
}
RowLayout{
ColumnLayout{
Label {
text: qsTr("")
font.pointSize: 10
width: 50
}
}
TouchSlider {
width: sliderWidth
sliderTitle: "Price"
sliderMin: priceMin
sliderMax: priceMax
sliderVal: priceValue
}
TouchSlider {
width: sliderWidth
sliderTitle: "Quality"
sliderMin: qualityMin
sliderMax: qualityMax
sliderVal: qualityValue
}
}
}
}
main.qml:
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("SliderPair Test")
Item {
PlayerControls{
playerName: "Player 1"
priceMin: 0
priceMax: 200
priceValue: 100
qualityMin: 0
qualityMax: 50
qualityValue: 25
sliderWidth: 200
}
}
}
The result should look like this:
On a touch screen like my Surface Pro, I can control each slider simultaneously with two fingers. Since Windows supports up to 10 simultaneous touches that should mean I can have 2-4 players without a problem. We shall see.
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 just beginning to learn C++ and Qt Framework in particular and I already have a problem right there. The question is how do I create and display data which is not just a string but rather an object, which properties I can access and display. E.g I have a list of employees and I want to display a list which looks like this:
---------------------
John Smith
Salary: 50,230
---------------------
Max Mustermann
Salary: 67,000
---------------------
The goal is that each item in the list is clickable and opens a new window with the details. Also, the important part is that I can be able to style the properties differently.
Qt provide us model and view frameworks, it is pretty flexible.
You could save your data by "model", show the data of your "model" by "view"
and determine how to play your data by "delegate"
The codes of c++ is a little bit verbose, so I use qml from the document to express the idea
import QtQuick 2.1
import QtQuick.Window 2.1
import QtQuick.Controls 1.0
Rectangle {
width: 640; height: 480
//the new window
Window{
id: newWindow
width: 480; height:240
property string name: ""
property string salaryOne: ""
property string salaryTwo: ""
Rectangle{
anchors.fill: parent
Text{
id: theText
width:width; height: contentHeight
text: newWindow.name + "\nSalaryOne : " + newWindow.salaryOne + "\nSalaryTwo : " + newWindow.salaryTwo
}
Button {
id: closeWindowButton
anchors.centerIn: parent
text:"Close"
width: 98
tooltip:"Press me, to close this window again"
onClicked: newWindow.visible = false
}
}
}
ListModel {
id: salaryModel
ListElement {
name: "John Smith"
SalaryOne: 50
SalaryTwo: 230
}
ListElement {
name: "Max Mustermann"
SalaryOne: 67
SalaryTwo: 0
}
}
//this is the delegate, determine the way you want to show the data
Component {
id: salaryDelegate
Item {
width: 180; height: 40
Column {
Text { text: name }
Text { text: "Salary : " + SalaryOne + ", " + SalaryTwo }
}
MouseArea{
anchors.fill: parent
//set the value of the window and make it visible
onClicked: {
newWindow.name = model.name
newWindow.salaryOne = model.SalaryOne
newWindow.salaryTwo = model.SalaryTwo
newWindow.visible = true
view.currentIndex = index
}
}
}
}
ListView {
id: view
anchors.fill: parent
model: salaryModel
delegate: salaryDelegate
}
}
You could separate the window or ListView into different qml files, combine the power of c++ ,qml and javascript. Declarative langauge like qml is pretty good on handling UI.
c++ version
#include <memory>
#include <QApplication>
#include <QListView>
#include <QSplitter>
#include <QStandardItemModel>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QStandardItemModel model(2, 1);
model.appendRow(new QStandardItem(QString("John Smith\nSalary: %1, %2\n").arg(50).arg(230)));
model.appendRow(new QStandardItem(QString("Max Mustermann\nSalary: %1, ").arg(67) + QString("000\n")));
QSplitter splitter;
QListView *list = new QListView(&splitter);
list->setModel(&model);
splitter.addWidget(list);
splitter.show();
return a.exec();
}
Enhance them by your need, c++ version also support delegate.
You could encapsulate the QListView and open a new window when the
user click on the index(you need QItemSelectionModel to detect which
item you selected).Before you can design higly customize UI,you have
to study a lot of the model and view frameworks of Qt. Since your case
are pretty simple, default QListView and QStandardItemModel is enough.
Supplement : How to detect which index you selected?
//the type of model_selected is QItemSelectionModel*
model_selected = list->selectionModel();
connect(model_selected, SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
this, SLOT(selection_changed(QItemSelection, QItemSelection)));
void imageWindow::selection_changed(QItemSelection, QItemSelection)
{
//do what you want
}
I've got following code:
main.cpp
QDeclarativeView *qmlView = new QDeclarativeView();
qmlView->setSource(QUrl("qrc:/nav.qml"));
ui->nav->addWidget(qmlView);
Blockschaltbild bild;
QObject *value = qmlView->rootObject();
QObject::connect(value, SIGNAL(testSig()), &bild, SLOT(BlockSlot()));
The signals and slots connect correctly. (QObject::connect returns "true")
qml file:
Rectangle {
id: rectangle1
....
signal testSig()
MouseArea{
id: mousearea
anchors.fill: parent
onEntered: parent.color = onHoverColor
onExited: parent.color = parent.buttonColor
onClicked: {
rectangle1.testSig()
console.log("Button clicked")
}
}
}
This is where the slot is located:
Blockschaltbild.h
class Blockschaltbild: public QObject
{
Q_OBJECT
public slots:
void BlockSlot(){
cout << "Slot is working" << endl;
}
public:
....
}
If I click on the mouse area, the console shows "Button clicked" but not "Slot is working".
I use Qt 4.8.4 with QtQuick 1.1. Where is my mistake?
If you simply require to work with your Blockschaltbild object in QML, you can also decide against a loose coupling with signal and slots and simply pass your object as a context parameter, to make it available in QML.
QDeclarativeView *qmlView = new QDeclarativeView();
qmlView->setSource(QUrl("qrc:/nav.qml"));
ui->nav->addWidget(qmlView);
Blockschaltbild* bild;
QObject *value = qmlView->engine().rootContext()->setContextProperty("bild", bild);
You can then call the BlockSlot() slot of your object from QML with:
Rectangle {
id: rectangle1
....
MouseArea{
id: mousearea
anchors.fill: parent
onEntered: parent.color = onHoverColor
onExited: parent.color = parent.buttonColor
onClicked: {
bild.BlockSlot() // call BlockSlot of "bild" context property
console.log("Button clicked")
}
}
}
It is also possible to use qmlRegisterType, which allows you to create instances of your Blockschaltbild class with QML. See here for more information.
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"
}
}
}