Drawing multiple rectangles into image in QML - qt

I am trying to draw rectangles into QML. Data, which contains info about those rectangles, looks like this:
X
Y
Width
Height
Data
Data are stored in array and each item in array represents one rectangle. I am looking for best (or at least a good) way to draw those rectangles.
Which component of QML should I use?
class.h
class MyClass : public QObject
{
Q_OBJECT
Q_PROPERTY(QList<structure> list READ list NOTIFY listChanged)
public:
QList<structure> list() const { return list_; }
signals:
listChanged();
private:
QList<structure> list_;
}
repeater.qml
Repeater {
model: 2
delegate: Rectangle{
width: model.list.width
height: model.list.height
x: model.list.x
y: model.list.y
color: "red"
}
}

It is not necessary to create a QObject, just a QVariantList that stores the QRect is enough. On the other hand the model that you have to pass is just the list of QRect, to access each QRect in the delegate you must use modelData.
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QRect>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QVariantList rectlist;
rectlist<< QRect{50, 30, 100, 100}
<< QRect{200, 20, 30, 30}
<<QRect{300, 300, 200, 33}
<<QRect{400, 23, 44, 55};
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("rectlist", rectlist);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Repeater {
model: rectlist
delegate: Rectangle{
x: modelData.x
y: modelData.y
width: modelData.width
height: modelData.height
color: "red"
}
}
}
Update:
main.cpp
#include <QColor>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QRect>
struct Data
{
Q_GADGET
Q_PROPERTY(QRect rect MEMBER rect)
Q_PROPERTY(QString text MEMBER text)
Q_PROPERTY(QColor color MEMBER color)
public:
QRect rect;
QString text;
QColor color;
Data(const QRect& rect= QRect(), const QString& text="", const QColor& color = QColor(Qt::transparent)){
this->rect = rect;
this->text = text;
this->color = color;
}
};
Q_DECLARE_METATYPE(Data)
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QVariantList rectlist;
rectlist <<QVariant::fromValue( Data{ QRect{50, 30, 100, 100}, "text1", Qt::red});
rectlist <<QVariant::fromValue( Data{ QRect{200, 20, 30, 30 }, "text2", QColor("blue")});
rectlist <<QVariant::fromValue( Data{ QRect{300, 300, 200,33}, "text3", QColor(0, 200, 0)});
rectlist <<QVariant::fromValue( Data{ QRect{400, 23, 44, 55 }, "text4"});
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("rectlist", rectlist);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
#include "main.moc"
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Repeater {
model: rectlist
delegate: Rectangle{
x: modelData.rect.x
y: modelData.rect.y
width: modelData.rect.width
height: modelData.rect.height
color: modelData.color
Text{
anchors.centerIn: parent
text: modelData.text
}
}
}
}

Related

Why doesn't ComboBox show the Selected Item?

Here's what I've in my QML:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
Window {
visible: true
width: 640
height: 480
title: "Test Window"
ComboBox{
width: 300
model: testContext.List
delegate: ItemDelegate{
width: parent.width
contentItem: RowLayout{
Text{ text: modelData.name }
Text{
text: " | " + modelData.age
Layout.alignment: Text.AlignRight
}
}
background: Rectangle{ color: hovered? "green" : "white" }
}
}
}
When I click on the ComboBox I see items in the popup list BUT the selected item doesn't appear in the box!
If I set textRole: "name", it shows only the name property in the box but I want the whole formatted text, defined in ItemDelegate, in the box.
Here in the illustration they've one more contentItem beside the one in delegate:
contentItem: Text {
...
text: control.displayText
...
}
It still doesn't show the formatted text in the box even if I add the additional contentItem in my QML.
EDIT
Here's the ViewModel .h:
#ifndef TEST_H
#define TEST_H
#include <QObject>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QVector>
#include "aclass.h"
#include "Property.h"
class Test : public QObject
{
Q_OBJECT
PROPERTY(QVector<AClass*>, List)
public:
explicit Test(QObject *parent = nullptr);
private:
QQmlApplicationEngine engine;
};
#endif // TEST_H
the .cpp:
#include "test.h"
Test::Test(QObject *parent) : QObject(parent)
{
engine.rootContext()->setContextProperty("testContext", this);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
for(int i = 0; i < 10; i++){
auto a = new AClass();
a->setname("Item " + QString::number(i));
a->setage(i + 10);
m_List.push_back(a);
}
emit ListChanged();
}
and the main.cpp:
#include <QGuiApplication>
#include "test.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
Test test;
return app.exec();
}
for the Q_PROPERTY I've a macro PROPERTY with the following content:
#ifndef PROPERTY_H
#define PROPERTY_H
#define PROPERTY(QType, name) \
Q_PROPERTY(QType name READ name WRITE set##name NOTIFY name##Changed) \
public: \
QType name(){return m_##name;} \
void set##name(QType value){m_##name = value; emit name##Changed();} \
Q_SIGNAL void name##Changed(); \
private: \
QType m_##name;
#endif // PROPERTY_H
Here's AClass:
#ifndef ACLASS_H
#define ACLASS_H
#include <QObject>
#include "Property.h"
class AClass : public QObject
{
Q_OBJECT
PROPERTY(QString, name)
PROPERTY(int, age)
};
#endif // ACLASS_H
For that I've to use the additional contentItem outside the ItemDelegate like this:
contentItem: RowLayout{
Text{ text: model[control.currentIndex].name }
Text{
text: " | " + model[control.currentIndex].age
horizontalAlignment: Text.AlignRight
Layout.fillWidth: true
}
}
and I've to give the ComboBox an id, here in the example control is the id

How to get the className of activeFocusControl in QML ApplicationWindow

I try
ApplicationWindow {
onActiveFocusControlChanged: {
console.log(activeFocusControl)
console.log(activeFocusControl.objectName)
}
}
ouput:
qml: QQuickTextField(0xa6ec00) //the 'activeFocusControl'
qml: //the 'activeFocusControl.objectName'
qml: QQuickButton(0xd7ccb0)
qml:
I want to
onActiveFocusControlChanged: {
if (activeFocusControl.className == "QQuickTextField") {
//do something
}
else if (activeFocusControl.className == "QQuickButton") {
//do something
}
but the "className" method does not exist
so how i can do it?
sorry, my english is pool, and thank you
There is no method to access the className from qml, so a possible solution is to create a helper from c ++ as shown below:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QObject>
class Helper : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE QString getClassName(QObject *obj) const{
return obj? obj->metaObject()->className(): "";
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
#if defined(Q_OS_WIN)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
Helper helper;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("helper", &helper);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
then it is used on the QML side:
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.3
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Button {
id: button
x: 270
y: 47
text: qsTr("Button")
}
TextField {
id: textField
x: 220
y: 169
text: qsTr("Text Field")
}
onActiveFocusControlChanged: {
var className = helper.getClassName(activeFocusControl)
switch(className){
case "QQuickTextField":
console.log("I am QQuickTextField")
break
case "QQuickButton":
console.log("I am QQuickButton")
break
default:
console.log("empty")
}
}
}
The complete example can be found at the following link.

Run the QtQuick application

I create a project on QtQuick in QT Creator 4.3.1 without using the ui form.
Here's the code main.qml:
import QtQuick 2.6
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
MouseArea {
anchors.fill: parent
onClicked: {
console.log(qsTr('Clicked on background. Text: "' + textEdit.text + '"'))
}
}
TextEdit {
id: textEdit
text: qsTr("Enter some text...")
verticalAlignment: Text.AlignVCenter
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 20
Rectangle {
anchors.fill: parent
anchors.margins: -10
color: "transparent"
border.width: 1
}
}
}
The program runs and works.
Now I want to get rid of Window and replace it with Rectangle:
import QtQuick 2.6
Rectangle {
id: root
width: 200; height: 200;
color: "#ffffff"
}
But when the program starts, nothing happens, the form does not open
What am I doing wrong?
main.cpp code. code in the same in both cases:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
In a Qt application you must have at least one window that is the top-level, that is, the window where other components are placed.
When you create your project, you should get the default main.cpp:
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
}
So let's analyze the elements and use the documentation for it:
QQmlApplicationEngine:
QQmlApplicationEngine provides a convenient way to load an application from a single QML file.
[...]
Unlike QQuickView, QQmlApplicationEngine does not automatically create a root window. If you are using visual items from Qt Quick, you will need to place them inside of a Window.
That is, QQmlApplicationEngine does not create a top-level, so if we want the window to be shown you must use another element, and as recommended, an option is to use Window{}
In your second test you are using an item, e.g. Rectangle, and this is only a component and is not able to create a top-level, so for that it is advisable to use QQuickView:
The QQuickView class provides a window for displaying a Qt Quick user interface.
[...]
So if you want to show the Rectangle you should use the following in your main.cpp:
main.cpp
#include <QGuiApplication>
#include <QQuickView>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView view;
view.setSource(QUrl(QStringLiteral("qrc:/main.qml")));
view.setResizeMode(QQuickView::SizeRootObjectToView);
view.show();
return app.exec();
}
main.qml
import QtQuick 2.6
Rectangle {
id: root
width: 200; height: 200;
color: "#ffffff"
}

What is the most formal way to invoke C++ code in response to a QML button?

Using qt 5.5, qt quick controls 1.4 and the below qt creator boilerplate code: what is the most FORMAL way to invoke C++ code in response to a button (just debug text to screen)?
// main cpp
#include <QApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
and the QML file inside the qml.qrc:
import QtQuick 2.5
import QtQuick.Controls 1.4
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Button {
id: add
x: 248
y: 222
text: qsTr("add")
}
}
I am aware of this as a possible answer but it looks a very complicated way to just hook a button to a code! If this is The Formal way to use Qt 5.5 and QML then this should be the answer.
As you can see in the documentation, you have many options:
The class can be registered as an instantiable QML type. This was the option proposed by #BaCaRoZzo
The class can be registered as a Singleton Type
An instance of the class can be embedded into QML code as a context property or context object
The Qt QML module also provides ways to do the reverse and manipulate QML objects from C++ code. This was the option proposed by #hyde
In your case, I'd prefer the last option because it requires fewer lines of code.
Example:
main.cpp
// main cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "myclass.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QObject *item = engine.rootObjects().first();
MyClass myClass;
QObject::connect(item, SIGNAL(qmlSignal(QString)),
&myClass, SLOT(cppSlot(QString)));
return app.exec();
}
main.qml
import QtQuick 2.5
import QtQuick.Controls 1.4
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
signal qmlSignal(string msg)
Button {
id: add
x: 248
y: 222
text: qsTr("add")
onClicked: qmlSignal(text)
}
}
myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QObject>
#include <QDebug>
class MyClass : public QObject
{
Q_OBJECT
public slots:
void cppSlot(const QString &msg) {
qDebug() << "Called the C++ slot with message:" << msg;
}
};
#endif // MYCLASS_H
I made an example to show both approaches mentioned by #BaCaRoZzo :
// main.cpp
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "myclass.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
MyClass myclass;
engine.rootContext()->setContextProperty("_myclass", &myclass);
QObject *item = engine.rootObjects().first();
QObject::connect(item, SIGNAL(qmlSignal(QString)), &myclass, SLOT(cppSlot(QString)));
return app.exec();
}
The header file of the c++ class that is invoked from qml:
// myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QObject>
class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass(QObject *parent = 0);
signals:
public slots:
void count();
void cppSlot(const QString &msg);
};
#endif // MYCLASS_H
and its implementation:
#ifndef MY_CLASS_H
#define MY_CLASS_H
// myclass.cpp
#include "myclass.h"
#include <QDebug>
MyClass::MyClass(QObject *parent) : QObject(parent)
{
}
void MyClass::count()
{
static int i = 0;
i++;
qDebug() << "wow =" + QString::number(i) ;
}
void MyClass::cppSlot(const QString &msg)
{
qDebug() << "Called the C++ slot with message:" << msg;
}
#endif
The user interface qml file with two buttons that show both approaches:
//main.qml
import QtQuick 2.5
import QtQuick.Controls 1.4
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
signal qmlSignal(string msg)
Button {
id: button
x: 218
y: 229
width: 148
height: 31
text: qsTr("run cpp method ctxt prop")
onClicked: _myclass.count()
}
Button {
id: button1
x: 218
y: 300
width: 148
height: 23
text: qsTr("run cpp method qmlsignal")
onClicked: qmlSignal(text)
}
}

Custom Styling Qt Quick Controls

I would like to have a custom design for my Qt Quick Controls. For example, I would like to change background colour of a tool bar, since I hate the default design. How can I do that?
In Qt Quick Controls, there is limited styling available via Qt Quick Control Styles items, like ButtonStyle, CheckBoxStyle, etc.
At the moment, other styles require delving into Qt sources and messing with internal details.
Here is a complete example of how one might modify the toolbar's style.
main.qml
import QtQuick 2.1
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.0
ApplicationWindow {
toolBar: ToolBar {
id: toolbar
Component.onCompleted: toolbar.data[0].item.children = [newRectangle];
property Item _newRectangle: Rectangle {
// The rectangle within the ToolBarStyle's panel
// Gleaned from:
// http://qt.gitorious.org/qt/qtquickcontrols/source/
// c304d741a27b5822a35d1fb83f8f5e65719907ce:src/styles/Base/ToolBarStyle.qml
id: newRectangle
anchors.fill: parent
gradient: Gradient{
GradientStop{color: "#a00" ; position: 0}
GradientStop{color: "#aaa" ; position: 1}
}
Rectangle {
anchors.bottom: parent.bottom
width: parent.width
height: 1
color: "#999"
}
}
RowLayout {
ToolButton { iconSource: "image://images/img1" }
ToolButton { iconSource: "image://images/img2" }
}
}
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickWindow>
#include <QImage>
#include <QPainter>
#include <QQuickImageProvider>
#include <QDebug>
class ImageProvider : public QQuickImageProvider
{
public:
ImageProvider() : QQuickImageProvider(QQuickImageProvider::Image) {}
QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) {
QImage img(32, 32, QImage::Format_ARGB32_Premultiplied);
img.fill(0); // transparent
QPainter p(&img);
p.setRenderHint(QPainter::Antialiasing);
p.translate(16, 16);
p.scale(14, 14);
p.setPen(QPen(Qt::black, 0.1));
if (id == "img1") {
p.drawEllipse(QPointF(0, 0), 1, 1);
}
else if (id == "img2") {
p.drawLine(-1, -1, 1, 1);
p.drawLine(-1, 1, 1, -1);
}
*size = img.size();
return img;
}
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.addImageProvider("images", new ImageProvider);
engine.load(QUrl("qrc:///main.qml"));
QObject *topLevel = engine.rootObjects().value(0);
QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
window->show();
return app.exec();
}
main.qrc
<RCC>
<qresource prefix="/">
<file>main.qml</file>
</qresource>
</RCC>
I think the following lines are completely useless:
QObject *topLevel = engine.rootObjects().value(0);
QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
window->show();
You just need to do something like:
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();

Resources