I am a newbie to QT but have a bit of experience with C++. this is my first project with Qt and QML.
I want to develop a program that lets the user draw on a canvas (see picture below) and I would like to calculate the area of each contour. I figured out how-to-draw on a canvas.
Example 1 Picture shows a Red rectangle which is the canvas area where the user will do a free-form sketch. The user draws the black free-form sketch.
Now I would like to calculate the area on the left and right contour. User will always only draw from one edge to another edge.
Below is another example of the free-form sketch. In the following sketch, I need to calculate the area of the three contours.
also, the contours can intersect with the same edge and other contour.
My question is, what is the best way to calculate the area of each contour?
should I use a contour detection algorithm or OpenCV?
any thoughts on how i should tackle this problem.
My initial thoughts are
start at canvas position (0,0) and start counting until I reach one point drawn by the user and repeat until I reach the end
After watching a couple of YouTube videos, I figured out the interaction between C++ and QML. now I can access the mouse position in C++. (see code below)
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlEngine>
#include <QQmlContext>
#include "canvasarea.h"
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QScopedArrayPointer<CanvasArea> canvasArea(new CanvasArea);
engine.rootContext()->setContextProperty("canvasArea", canvasArea.data());
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Row {
id: colorTools
anchors {
horizontalCenter: parent.horizontalCenter
top: parent.top
topMargin: 8
}
property color paintColor: "#000000"
spacing: 4
Button {
text: "Clear"
onClicked: {
canvas.clear()
canvasArea.clearCanvas();
}
}
}
Rectangle{
anchors.fill: canvas
border.color: "#ff0000"
border.width: 4;
}
Canvas {
id: canvas
width: 640
height: 480
anchors {
left: parent.left
top: colorTools.bottom
margins: 8
}
property real lastX
property real lastY
property color color: colorTools.paintColor
function clear() {
var ctx = getContext('2d')
ctx.reset();
canvas.requestPaint();
}
onPaint: {
var ctx = getContext('2d')
ctx.lineWidth = 1.5
ctx.strokeStyle = canvas.color
ctx.beginPath()
ctx.moveTo(lastX, lastY)
lastX = area.mouseX
lastY = area.mouseY
ctx.lineTo(lastX, lastY)
ctx.stroke()
canvasArea.mouseCordinates(lastX, lastY)
}
MouseArea {
id: area
anchors.fill: parent
onPressed: {
canvas.lastX = mouseX
canvas.lastY = mouseY
}
onPositionChanged: {
canvas.requestPaint()
}
}
}
}
canvasarea.cpp
#include "canvasarea.h"
#include <QDebug>
CanvasArea::CanvasArea(QObject *parent) : QObject(parent)
{
}
void CanvasArea::mouseCordinates(int x, int y)
{
QPoint point(x , y);
userContourLocations.append(point);
qDebug() << "currentCursorPositon" << point;
}
void CanvasArea::clearCanvas()
{
userContourLocations.clear();
}
Related
I want to draw a plot of sensor data that is saved in a QList. I know that getting the data up to the qml level works fine, because it correctly prints to console (see code).
My question now is, how can I draw a LineChart out of this data? I tried a repeater and some sketchy for- loops but none seem to work and only an empty chart is displayed.
As stated above, the data works and is logged correctly. The axis are correct as well, there just is no line.
My most promising try:
import QtQuick 2.0
import QtCharts 2.3
Item {
width:400
height:600
ChartView
{
width:parent.width
height:parent.height
Component.onCompleted: {
//Data displayed correctly
console.log(device.AIN3data.length);
console.log(device.AIN3data)
}
ValueAxis {
id: axisX
min: 0
max: device.AIN3data.length
}
ValueAxis {
id: axisY
min: 0
max: 1000
}
LineSeries
{
axisX: axisX
axisY:axisY
name: "AIN3"
Repeater
{
model: device.AIN3data.length
XYPoint{x:index;y:device.AIN3data[index]}
Component.onCompleted: console.log("Repeater")
}
}
}
}
Repeater is used to generate several Items, not for XYPoint. In this case it is better to create a C++ model, export it to QML and then map to a LineSeries using VXYModelMapper.
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QStandardItemModel>
#include <QTimer>
#include <random>
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QApplication app(argc, argv);
QStandardItemModel model(0, 2);
QTimer timer;
timer.setInterval(1000);
QObject::connect(&timer, &QTimer::timeout, &model, [&model](){
static std::default_random_engine e;
static std::uniform_real_distribution<> dis(0, 1000);
QStandardItem *xItem = new QStandardItem;
QStandardItem *yItem = new QStandardItem;
xItem->setData(model.rowCount(), Qt::DisplayRole);
yItem->setData(dis(e), Qt::DisplayRole);
model.appendRow({xItem, yItem});
});
timer.start();
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("dataModel", &model);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
import QtCharts 2.3
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
ChartView {
anchors.fill: parent
ValueAxis {
id: axisX
min: 0
max: lineSeries.count - 1
}
ValueAxis {
id: axisY
min: 0
max: 1000
}
LineSeries {
id: lineSeries
axisX: axisX
axisY: axisY
}
}
VXYModelMapper {
id: modelMapper
model: dataModel
series: lineSeries
xColumn: 0
yColumn: 1
}
}
the code below shows two C++ QStringListModels.
"model" is integrated into the main.qml file as "myModel" and "model2" is integrated into the windowLoader.qml file as "myModel2".
The View of "MyModel2" is loaded when you click on an item of the View of "myModel".
My Question:
If there would be a "myModelX", is it possible to change the model property from windowLoader.qml which has the value "myModel2" or do I have to write a windowLoader.qml for every Model?
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QStringListModel>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QStringListModel model;
model.setStringList( QStringList() << "foo" << "bar" << "baz" );
QStringListModel model2;
model2.setStringList( QStringList() << "a" << "b" << "c" );
QQmlApplicationEngine engine;
QQmlContext *context = engine.rootContext();
context->setContextProperty( "myModel", &model );
context->setContextProperty( "myModel2", &model2);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
visible: true
width: 640
height: 480
title: qsTr("StringList")
Loader {
id: windowLoader
}
ListView {
anchors.fill: parent
model: myModel
delegate: Rectangle {
id: rec1
property alias text: text_field.text
color: "green"
border.color: "lightgreen"
radius: 8
width: 100
height: 20
MouseArea {
anchors.fill: parent
onClicked: windowLoader.setSource( "qrc:/windowLoader.qml",
{ "title_text":
rec1.text } )
}
Text {
id:text_field
anchors.centerIn: parent
text: display
}
}
}
}
windowLoader.qml
import QtQuick 2.0
import QtQuick.Window 2.0
Window {
visible: true
width: 640
height: 480
title: qsTr(title_text + "-Window")
property string title_text
ListView {
anchors.fill: parent
model: myModel2
delegate: Rectangle {
color: "blue"
border.color: "lightsteelblue"
radius: 8
width: 100
height: 20
Text {
anchors.centerIn: parent
text: display
}
}
}
}
I am trying to operate a change of text after a ProgressBar finishes its loop as shown below.
The actual result I have is the following:
The expected result would be the one below:
Below the code of the minimal verifiable example. I am handling the ProgressBar as an external C++ class.
In case needed the small verifiable example can be found here in case needed:
main.qml
import QtQuick 2.4
import QtQuick.Controls 2.12
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.12
import QtQuick.Controls.impl 2.12 // for IconLabel
ApplicationWindow {
id: window
width: 640
height: 480
visible: true
// property var changeText
// color: changeText ? changeText.color : "green"
function buttonClick(button)
{
button.text = qsTr(" YES Connecting").arg(button.text);
button.enabled = false;
if (button.background && button.background instanceof Rectangle) {
button.background.color = "green";
button.background.gradient = null;
button.background.visible = true;
}
if (button.contentItem && button.contentItem instanceof IconLabel) {
button.contentItem.color = "white";
button.contentItem.font.bold = true;
button.contentItem.font.pointSize = 20;
}
}
function textConnectedChange(textChange1)
{
textChange1.text = qsTr("Connecting...");
textChange1.color = "blue";
textChange1.content = "Connected";
textChange1.font.bold = true;
textChange1.font.pointSize = 15;
}
QtObject {
id: test
property color color: "grey"
}
ColumnLayout {
Button {
id: dialogA
text: pBar.running ? "Connecting..." : "Not - Connected"
Layout.fillWidth: true
font.pointSize: 20
spacing: 10
onClicked: {
buttonClick(this)
pBar.startComputation()
}
}
ColumnLayout {
id: layout
Layout.fillWidth: true
spacing: 10
GroupBox {
id: box1
width: parent.width
title: "Connection"
font.pointSize: 20
Layout.fillWidth: parent
spacing: 10
GridLayout {
width: parent.width
columns: 1
RowLayout {
id: row1
spacing: 200
Layout.fillWidth: true
Layout.fillHeight: false
Label {
id: textField
text: "Connection:"
font.pointSize: 15
Layout.fillWidth: true
}
Text {
id: connected
text: pBar.running ? textConnectedChange(this) : "Not-Connected"
color: "red"
font.pointSize: 15
horizontalAlignment: Text.AlignRight
Layout.fillWidth: true
onTextChanged: {
text: parent.changeText = test
}
}
}
}
}
GroupBox {
id: boxprogress
title: "Connection Progress"
font.pointSize: 20
Layout.fillWidth: parent
width: parent.width
spacing: 10
GridLayout {
width: parent.width
columns: 1
RowLayout {
Layout.fillWidth: true
Layout.fillHeight: false
ProgressBar {
id: progressbar_id
Layout.fillWidth: true
Layout.fillHeight: true
width: parent.width
from: 0
to: 40
value: pBar.progress
}
}
}
}
}
}
}
progressbar.h
#ifndef PROGRESSBAR_H
#define PROGRESSBAR_H
#include <QObject>
#include <QFutureWatcher>
class ProgressBar : public QObject
{
Q_OBJECT
Q_PROPERTY(float progress READ progress NOTIFY progressChanged)
Q_PROPERTY(bool running READ running NOTIFY runningChanged)
Q_PROPERTY(bool finished READ finished NOTIFY finishedChanged)
public:
ProgressBar();
float progress();
bool running();
bool finished();
public Q_SLOTS:
void startComputation();
void cancelComputation();
void finishComputation();
private Q_SLOTS:
void updateProgress(int value);
signals:
void progressChanged();
void runningChanged();
void finishedChanged();
private:
bool m_running = false;
int m_progressValue = 0;
int m_finished = true;
QVector<int> vector;
QObject m_Model;
QFutureWatcher<void> m_futureWatcher;
};
#endif // PROGRESSBAR_H
main.cpp
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "progressbar.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
ProgressBar pBar;
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
engine.rootContext()->setContextProperty("pBar", &pBar);
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
What I have done so far:
1) The following source was useful in understanding the problem but didn't bring any beneficial correction.
2) this post I found to be useful in order to modify the necessary bindings to change a text but didn't work
3) I consulted the official documentation regarding the QML bindings and tried to apply directly from there but something is not correct.
4) You see QObject in the code and that is because from here I thought I had to use it. I left it in the code to show you what additional trials I was trying to do, but that didn't work.
Please point in the right direction to solve this problem.
QML has a State object, which is perfectly suited for handling this kind of problem, I mean, you want different bindings in different "states".
The following example should get you quite somewhere, however, I'm not 100% I extracted everything correctly from your example:
Text {
id: connected
text: qsTr("Not-Connected")
color: "red"
font.pointSize: 15
horizontalAlignment: Text.AlignRight
Layout.fillWidth: true
states: [
State {
name: "connecting"
when: pBar.running
PropertyChanges {
target: connected
text: qsTr("Connecting...")
color: "blue"
font.bold: true
}
},
State {
name: "connected"
when: pBar.finished
PropertyChanges {
target: connected
text: qsTr("Yes! Connected...")
color: "green"
font.bold: true
}
}
]
}
I have this code in Qt
int main(int argc, char *argv[]) {
QGuiApplication app(argc,argv);
QQuickView view;
QUrl q(QStringLiteral("QML:///places_map.qml"));
view.setSource(QUrl(QStringLiteral("qrc:///places_map.qml")));
//I've tried this to init qml properties
QObject *object = view.rootObject();
object->setProperty("latitude", 48.4656371);
object->setProperty("longitude", 31.04900455);
QQuickItem *item = view.rootObject();
item->setProperty("device_latitude", 48.4656371);
item->setProperty("device_longitude", 35.04900455);
}
And my qml file:
import QtQuick 2.0
import QtPositioning 5.5
import QtLocation 5.6
Rectangle {
width: 720
height: 480
property double latitude: 0
property double longitude: 0
property double device_latitude: 0 //48.4656371
property double device_longitude: 0 //35.54900455
property variant location: QtPositioning.coordinate(latitude, longitude)
property variant deviceLocation: QtPositioning.coordinate(device_latitude, device_longitude)
Plugin {
id: myPlugin
name: "osm"
}
PlaceSearchModel {
id: searchModel
plugin: myPlugin
searchTerm: "Pizza"
searchArea: QtPositioning.circle(deviceLocation)
Component.onCompleted: update()
}
Map {
id: map
anchors.fill: parent
plugin: myPlugin;
center: location
zoomLevel: 13
MapItemView {
model: searchModel
delegate: MapQuickItem {
coordinate: deviceLocation //QtPositioning.coordinate(device_latitude, device_longitude)
anchorPoint.x: image.width * 0.5
anchorPoint.y: image.height
sourceItem: Column {
Image { id: image; source: "marker.png" }
Text { text: title; font.bold: true }
}
}
}
}
}
In qml code properties double latitude and longitude set the view on map
but map shows place with latitude = 0 and longtitude = 0
If I set the correct coordinates in qml code everything works
How can I init this value from c++ code so that map will show my city ?
Using Q_PROPERTY would be better. Exposing Attributes of C++
I have it working like this:
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickItem>
#include <QQuickView>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView view;
view.setSource(QUrl(QStringLiteral("qrc:/main.qml")));
QQuickItem *item = view.rootObject();
item->setProperty("number", 22.002);
view.show();
return app.exec();
}
main.qml
import QtQuick 2.4
import QtQuick.Window 2.2
Rectangle{
visible: true
width: 640
height: 480
property double number : 0
Text {
id: textEdit
text: number
verticalAlignment: Text.AlignVCenter
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 20
}
}
I have two QML files in my project.
main.qml:
Window{
width: 800
height: 500
visible: true
id:mainWinenter
Item {
id: inlinecomponent
Rectangle {
id: display
width: 50; height: 50
color: "blue"
}
}
MouseArea {
anchors.fill: parent
onClicked: {
var component = Qt.createComponent("qrc:/Test.qml");
var object = component.createObject(inlinecomponent, {"x": 50, "y": 50});
}
} }
and my Test.qml file:
Item{
id:mc
Rectangle{
x: 0
y: 0
id: rec
width: 150; height:75
color: "grey"
objectName: "recc"
visible: true
}}
main.cpp:
int main(int argc, char *argv[]){
QApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QQuickView view;
view.setSource(QUrl("qrc:/Test.qml"));
QQuickItem *object = view.rootObject();
QObject *rect = object->findChild<QObject*>("recc");
if (rect)
rect->setProperty("color", "red");
view.show();
return app.exec();}
In main.cpp I use setProperty function to change the color of rectangle in Test.qml file. The color changes when I load the file in new window but it doesn't change when it loads in main.qml file. How I can change it when it loads in main.qml?
My output contain two windows. One for main.qml and one for Test.qml.
this is my main.qml output
this is my Test.qml output
The color of the rectangle in my Test.qml window is red but it loads grey in my main.qml. I want to load it red in main file and I want to do this with my cpp code.