How to move a rectangle component in qml - c++

How do I move a rectangle component in Qml using c++ program, it has to progress from minimum to maximum value like a progress bar with color gradient. i have tried to use number animation and it is working fine,but how do i change the color as it progresses.

It's hard to give a specific answer as you've not provided much detail or a code sample. However it seems like you want to set the color property of your progress bar rectangle to be dependent on it's width or position, so that it changes depending on how much 'progress' has been made.
In addition, you may be able to use a ColorAnimation class to animate this, along with a Behaviour, for example:
Rectangle {
id: progressBar
width: 0
height: 20
color: (width < 30) ? "red" : (width < 60) ? "yellow" : "green"
Behavior {
ColorAnimation { target: progressBar; duration: 500 }
}
}

Related

qml canvas, context 2d is reset after every calling js function from c++

I have Qt Quick Controls 2 Application. In main.qml I have besides other things canvas in scroll view:
Rectangle {
id: graph
width: mainArea.width / 3 - 14;
height: mainArea.height - 20;
ScrollView{
anchors.fill: parent;
Canvas {
id:canvasGraph;
width: graph.width;
height: graph.height;
property bool paintB: false;
property string colorRect: "#FFFF40";
property string name: "ELF header";
property int paintX: 0;
property int paintY: 0;
property int widthP: 160;
property int heightP: 30;
property int textX: (paintX + (widthP / 2)) - 15/*func return int length of text*/;
property int textY: (paintY + (heightP / 2)) + 3;
onPaint:{
if (paintB){
var ctx = canvasGraph.getContext('2d');
ctx.beginPath();
ctx.font = "normal 12px serif";
ctx.fillStyle = colorRect;
ctx.strokeRect(paintX, paintY, widthP, heightP);
ctx.fillRect(paintX, paintY, widthP, heightP);
ctx.strokeText("ELF header", textX, textY);
ctx.closePath();
ctx.save();
}
}
MouseArea{
id: canvasArea;
anchors.fill: parent;
onPressed: {
paint(mouseX,mouseY,"aaa",1);
}
}
}
}
}
At first I tried draw into canvas by js function, here:
function paint(x, y, name, type) {
canvasGraph.paintB = true;
canvasGraph.paintX = x;
canvasGraph.paintY = y;
canvasGraph.requestPaint();
}
This function was called by pressing mouse on canvas. It works good, it draw rectangles, one by one. But only one problem was, that after resizing app window, all rectangles except last one get lost. But it's not primary problem, because it works and this promblem I could resolve later.
For drawing chart I need C++ library (ELFIO, for reading ELF files). So in main.cpp I have two object. First allows me call from main.qml functions of some C++ class. Second allows me calling js functions from C++. Here is main.cpp:
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QScopedPointer<elfFile> elfFileObj(new elfFile);
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
engine.rootContext()->setContextProperty("elfFileObj", elfFileObj.data()); //this is for calling c++ from qml
QObject *rof = engine.rootObjects().first();
elfFileObj.data()->rofS = rof; //this one is for calling js func from c++
return app.exec();
How you can see, reading ELF files is manage from object elfFileObj, where is public variable which holds loaded ELF file and variable rofS which hold object for access to main.qml to js functions.
In elfFileObj is Q_INVOKABLE int loadELF(QString fileName); where Q_INVOKABLE is macro, which ensure, that this function is possible call from qml file. Function:
int elfFile::loadELF(QString fileName)
{
string fileNameReal = (fileName).toStdString().substr(7);
if (!reader.load(fileNameReal.c_str())){
return -1;
}
QVariant x(30);
QVariant y(10);
QVariant name("ELF header");
QVariant type(1);
QMetaObject::invokeMethod(rofS, "paint", Q_ARG(QVariant,x), Q_ARG(QVariant,y), Q_ARG(QVariant,name), Q_ARG(QVariant,type));
y = QVariant(40);
QMetaObject::invokeMethod(rofS, "paint", Q_ARG(QVariant,x), Q_ARG(QVariant,y), Q_ARG(QVariant,name), Q_ARG(QVariant,type));
}
I try draw two rectangles, one by one. QMetaObject::invokeMethod should call js functions, which draw rectangle on (x,y). Other args are in this moment unusable.
Main problem: It draw rectangles on canvas, but after every call by invokeMethod is canvas cleared. So on the canvas always stay only last one rectangle.
Have somebody any idea, how to save actual state of canvas? Thanks for any help.
It isn't pretty code, but it's my first experience with qml.
The canvas, being an imperative drawing API, just has a dumb buffer of pixel data. It has no concept of objects like rectangles or anything else once the draw call has finished. As such, you are responsible for everything that it displays via your onPaint handler. It does not clear the canvas content from one frame to another (as an optimization), but it will (by necessity) clear it when you resize the window, as it has to allocate a differently sized buffer.
You can see this behaviour here:
import QtQuick 2.6
Canvas {
id: canvasGraph;
width: 500
height: 500
property int paintY: 10;
onPaint:{
var ctx = canvasGraph.getContext('2d');
ctx.beginPath();
ctx.font = "normal 12px serif";
ctx.fillStyle = "#ff0000";
ctx.strokeRect(10, paintY, 160, 30);
ctx.fillRect(10, paintY, 160, 30);
ctx.closePath();
ctx.save();
}
Timer {
interval: 16
running: true
repeat: true
onTriggered: {
canvasGraph.requestPaint();
canvasGraph.paintY += 10
if (canvasGraph.paintY > canvasGraph.height)
canvasGraph.paintY = 10
}
}
}
Try run this example with qmlscene and resize the window. You'll notice that all content is cleared on resize, except that one single rectangle that it draws.
So, if you want all your rectangles to be retained, then you need to paint them in the onPaint handler each time (and make use of clearRect or some other method to fill the background to get rid of stuff that doesn't belong there anymore, if you are moving stuff around or making them invisible).
On the other hand, I can't directly explain why invokeMethod would be causing it to clear, as you haven't really presented enough code. It may be that it's resizing the canvas (causing the buffer to reallocate, and be cleared). Either way, given the above, I'd say that it isn't all that relevant.
After all this, while I don't have full background over what you are making, I'd suggest that perhaps Canvas might not be the best tool to do what you want. You might want to look into QQuickPaintedItem instead, or (better still) composing your scene using a custom QQuickItem which positions other QQuickItems (or QSGNodes). Canvas (and QQuickPaintedItem) while easy to use, are not especially performant.
I didn't solved this problem. I just stop using QML and return to use just Qt. It helps me.

Box2D center of rotation

I use Box2D as phisics engine and QtQuick as visualizer.
I setted up simple scene with falling small rectangles and platform to test collisions
// the platform block
Body {
id: block
bodyType: Body.Kinematic
width:200
height:20
transformOrigin: Body.Center
fixtures: Box {
anchors.fill: parent
friction: 1
density: 1
}
Rectangle {
anchors.fill: parent
color: "yellow"
border.color: "blue"
}
MouseArea {
anchors.fill: parent
onClicked: {
block.rotation += 20
}
}
}
In QML I can set center of rotation:
transformOrigin: Body.Center
By default transformOrigin is top-left corner of object and in this case everything paintet fine. But when i move origin to center in QML it is going wrong as decribed at attached image
This is part of code when it get coords from Box2D and paint QML object
//getting coordination and angle of Box2D object
const b2Vec2 position = mBody->GetPosition();
const float32 angle = mBody->GetAngle();
const qreal newX = position.x * scaleRatio;
const qreal newY = -position.y * scaleRatio;
const qreal newRotation = -(angle * 360.0) / (2 * b2_pi);
// paint QML object at received coords
setX(newX);
setY(newY);
setRotation(newRotation);
The problem is than Box2D rotates object with origin in top-left corner but QML object painted with origin in his center. So QML object is not syncronized with Box2D and falling rectangle painter in wrong place. I maked printscreen but actually box of Box2D is not visible, I added it to understand the problem.
And my question - how can I set origin point in Box2D?
To keep the code simple, and because qml-box2d is an unfinished library, transformOrigin set to TopLeft (the default for a Body) is currently the only supported value.
However, a Box2D body doesn't need to have a size itself. Only the size of its fixtures is relevant. If you set a 0-size on the body its transformOrigin no longer matters. If you then center the fixture on the body, the rotation of the body effectively applies to the center of the fixture.
I've adapted your simple scene using this approach as follows:
// the platform block
Body {
id: block
bodyType: Body.Kinematic
fixtures: Box {
id: boxFixture
anchors.centerIn: parent
width:200
height:20
friction: 1
density: 1
}
Rectangle {
anchors.fill: boxFixture
color: "yellow"
border.color: "blue"
}
MouseArea {
anchors.fill: boxFixture
onClicked: {
block.rotation += 20
}
}
}
Of course, ideally qml-box2d would eventually handle all possible values of transformOrigin regardless of the size of the body.

Spacing items on a row in QML?

I can move the "indicator" anywhere I want, but the "Button" and "DiagValue" I ONLY seem to be able move to different rows... I need to move to different columns or different "x".
Row{
spacing: table.rowSpacing
Button {
width: 60
//x: 100 //this does NOTHING!
//y: 159
text: tr_NOOP("Help")
onClicked: {
app.dialogs.message.title = trans("Water Status")
app.dialogs.message.body = trans("Rate is not met.")
app.dialogs.message.show();
}
}
Indicator {
x: -50
//y: 159
ok: false
}
DiagValue {
MeasuredValue {
id: lowFlowValueText
rawValueUnit: "gpm"
unit: "gpm"
precision: 1
}
label: trans("Water Flow Setting")
value: lowFlowValueText.text
}
}
I think if you set proper spacing in Row and align it horizontally, it should look like what you need.
Row {
spacing: table.rowSpacing
anchors.horizontalCenter: parent.horizontalCenter
...
}
And you don't need to supply x value when your elements are inside Row, Row element will decide x value based on spacing and width of individual child.
A Row will define the horizontal location and the horizontal extent (size) of the elements that it contains. It doesn't make sense to manually try to override either x or width for elements in a row, in fact it will generate warnings.
You can, of course, override the vertical position within a Row.
Your elements must be supplying sensible default sizes and must react sensibly to being resized, so without seeing how you define the components (Button, Indicator, DiagValue), it'd be impossible to say exactly where the problem is. For stock controls, it works fine.

How to change the color of the text of a QProgressBar with its value?

I don't know how to change the color of the text partially in the progress bar when its value becomes nearly 50%. This effect comes automatically in the fusion style progress bar (picture below). Does anyone know how this is done ?
Too lazy to write working example code, much less making a screenshot. Not even for 50 reps. :-)
However, the question was somewhat interesting. I had no idea how such a two colored text could be done. So I checked:
http://qt.gitorious.org/qt/qtbase/blobs/stable/src/widgets/styles/qfusionstyle.cpp
Line 1450ff (http://qt.gitorious.org/qt/qtbase/blobs/stable/src/widgets/styles/qfusionstyle.cpp#line1450).
QRegion rightRect = rect;
rightRect = rightRect.subtracted(leftRect);
painter->setClipRegion(rightRect);
painter->setPen(flip ? alternateTextColor : textColor);
painter->drawText(rect,
bar->text,
QTextOption(Qt::AlignAbsolute|
Qt::AlignHCenter|
Qt::AlignVCenter));
if (!leftRect.isNull())
{
painter->setPen(flip ? textColor : alternateTextColor);
painter->setClipRect(leftRect);
painter->drawText(rect,
bar->text,
QTextOption(Qt::AlignAbsolute|
Qt::AlignHCenter|
Qt::AlignVCenter));
}
Basically the text is drawn two times into the same rectangle. Each time with an appropriate clipping. Easy if you know how. :-)
From my point of view the best, and probably the easiest, way to do this is to change the pallet for the QProgressBar widget:
QPalette palette = progressBar->palette()
palette.setColor(QPalette::Text, textColor)
palette.setColor(QPalette::HighlightedText, textColor)
progressBar->setPalette(palette)
"The setBackgroundRole method let you use a color role for the background, which means one of the predefined color of the style applied to the widget. So your are basically limited to the style and its colors."
Background solution:
value = 65
self.progressBar.setProperty("value", value)
if value < 50:
self.progressBar.setStyleSheet("QProgressBar::chunk { background-color: black; }")
else:
self.progressBar.setStyleSheet("QProgressBar::chunk { background-color: black; } QProgressBar { color: white; }")
You can use stylesheet on the Container Widget :
myMainWidget.setStyleSheet(QString("QProgressBar {color: red}"));

QML ListView count is zero and only some rows are visible

I have created a ListView model based on QAbstractListModel similar to this example.
The problem is my ListView doesn't show all rows, but only those which are initialy visible. So when I scroll (or flick) down theres only black space. ListView has count == 0 but it should have count == 10, since I've added 10 elements.
My class contains the necessary mothods to update the model rowCount
// needed as implementation of virtual method
int rowCount(const QModelIndex & parent) const {
return standings.size();
}
int rowCount() {
return standings.size();
}
void addStanding(const Standing &st) {
beginInsertRows(QModelIndex(), rowCount(), rowCount());
qDebug() << "row cnt: " << rowCount();
standings << st;
endInsertRows();
}
I also update the ListView model
rootContext = (QDeclarativeContext*)(viewer->rootContext());
rootContext->setContextProperty("myModel", &sm);
QML code
ListView {
id: raceList
objectName: "standingList"
y: header.height
width: parent.width
height: parent.height - header.height
clip: true
model: myModel
delegate: rowComp
}
Component {
id: rowComp
SessionRowDelegate { }
}
I would also like to mention that this works fine with a static model.
How to make ListView display all items all the time and not only if visible to user?
SessionRowDelegate
Rectangle {
id: rowbg
width: parent.width
height: 34 * (1 + (parent.width - 360) * 0.002)
// contains other elements with height not exceeding rowbg's height
// ...
}
It is a bit late (4 years!), but there is something related to this problem that I have discovered after days of getting stuck! The problem is mainly based in the weird way ListView deals with the models.
After overriding the rowCount method in my own model, I faced the exact problem that you have mentioned. The funny thing is, they are NEVER called. ListView actually never calls to get the rowCount, instead queries for the amount in cacheBuffer size (which defaults to 512 on my system), that is why it won't notice if the items are more than the cacheBuffer size or the default it is using, it only notices that when it is scrolled to the end.
While this will shed light on some other people problems, it is not the solution for your problem. In your case, if the items are not that many, I suggest tweaking the ListView cacheBuffer size, and increasing it as much as you need. Just keep in mind that if your elements get numerous, it can have a huge performance penalty.
A ListView only renders the elements that are currently visible plus a few that are pre rendered to be shown when scrolling. The default implementation does not render all of them for performance reasons. Any references to the size of the list should refer to the model object instead of the ListView itself.