How to set dynamic QFont size? - c++

I came across QFontMetrics?
http://doc.qt.io/qt-5/qfontmetrics.html
This gives the height and width of the present font.
I need to run my application with full screen mode on on different monitors for which I am using Scale class. http://doc.qt.io/qt-5/qml-qtquick-scale.html
That returns the height and width of the current screen.
Is there a way to use QFontMetrics or anything else to change the font size according to the monitor size?
ApplicationWindow
{
id: head
visible: true
width: Screen.width
height: Screen.height
title: "Novus Pilot"
property var id: 0;
Draw_on_qimage
{
id: draw_on_qimage
anchors.fill: parent
parent: image
scaleX: head.width / 640
scaleY: head.height / 480
}
}
Draw_on_qimage is a cpp class.

The easiest way is to set QFont as Q_PROPERTY of your item so you can set it from QML:
#ifndef DRAWITEM_H
#define DRAWITEM_H
#include <QPainter>
#include <QQuickPaintedItem>
class DrawItem : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged)
public:
DrawItem(QQuickItem *parent = Q_NULLPTR):QQuickPaintedItem(parent){}
void paint(QPainter *painter){
painter->setFont(mFont);
painter->drawText(boundingRect(), "Hello");
}
QFont font() const{
return mFont;
}
void setFont(const QFont &font){
if(mFont == font)
return;
mFont = font;
emit fontChanged();
update();
}
signals:
void fontChanged();
private:
QFont mFont;
};
#endif // DRAWITEM_H
To set its size we use the pointSize property of QFont:
DrawItem
{
id: draw_on_qimage
anchors.fill: parent
font.pointSize: some_function(head.width, head.height)
transform: Scale {
xScale: head.width / 640
yScale: head.height / 480
}
}
Where some_function is the function that establishes the relationship between the font size and the size of the window.

Related

draw with the mouse on QQuickPaintedItem

I have this code
class cCanvas : public QQuickPaintedItem
..
..
void cCanvas::paint(QPainter *ppainter)
{
ppainter->setPen(QPen(colorValue()));
ppainter->drawLine(0, 0, rand() % 255 ,100);
}
QML
MultiPointTouchArea {
onPressed {
canvas.update();
}
}
It works, but when drawing each new line, the canvas is cleared, previous line is deleted. But I want the lines to stay.
QML Canvas have requestPaint();
How to call this method for QQuickPaintedItem?
How to correctly create the ability to draw with the mouse on QQuickPaintedItem?
(Drawing on the QML canvas itself is not suitable because of the speed of work, since drawing occurs using calculations)
Qt does not keep a cache of what was painted, so you only see what was painted last. A possible solution is to save the line instructions in a QPainterPath:
#ifndef CCANVAS_H
#define CCANVAS_H
#include <QPainterPath>
#include <QQuickPaintedItem>
class CCanvas : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
QML_ELEMENT
public:
CCanvas(QQuickItem *parent = nullptr);
Q_INVOKABLE void requestPaint();
void paint(QPainter *painter);
const QColor &color() const;
void setColor(const QColor &newColor);
signals:
void colorChanged();
private:
QPainterPath m_path;
QColor m_color;
};
#endif // CCANVAS_H
#include "ccanvas.h"
#include <QPainter>
CCanvas::CCanvas(QQuickItem *parent):QQuickPaintedItem(parent)
{
}
void CCanvas::requestPaint()
{
QPointF start(0, 0);
QPointF end(rand() % 255, 100);
m_path.moveTo(start);
m_path.lineTo(end);
update();
}
void CCanvas::paint(QPainter *painter)
{
painter->setPen(QPen(m_color));
painter->drawPath(m_path);
}
const QColor &CCanvas::color() const
{
return m_color;
}
void CCanvas::setColor(const QColor &newColor)
{
if (m_color == newColor)
return;
m_color = newColor;
emit colorChanged();
update();
}
import QtQuick 2.12
import QtQuick.Window 2.12
import Canvas 1.0
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
CCanvas{
id: canvas
anchors.fill: parent
color: "red"
}
Timer {
interval: 500; running: true; repeat: true
onTriggered: canvas.requestPaint()
}
}
QT += quick
CONFIG += c++11
SOURCES += \
ccanvas.cpp \
main.cpp
HEADERS += \
ccanvas.h
RESOURCES += qml.qrc
CONFIG += qmltypes
QML_IMPORT_NAME = Canvas
QML_IMPORT_MAJOR_VERSION = 1
A similar solution can be done with a QImage.

QQuickPaintedItem not visible in QML designer

I have created QQuickPaintedItem class that is responsible for painting UI stuff with overridable paint() method . The component is placed in Qml but i am not able to see it in designer.But i am able to see the result on runtime.
I am following https://doc.qt.io/qt-5/qtquick-customitems-painteditem-example.html
Nothing works , i really need to see the output in qml designer not on runtime.
As i am following https://doc.qt.io/qt-5/qtquick-customitems-painteditem-example.html
here is the source code for header file
class TextBalloon : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(bool rightAligned READ isRightAligned WRITE setRightAligned NOTIFY rightAlignedChanged)
public:
TextBalloon(QQuickItem *parent = 0);
void paint(QPainter *painter);
bool isRightAligned();
void setRightAligned(bool rightAligned);
private:
bool rightAligned;
signals:
void rightAlignedChanged();
};
and definition
TextBalloon::TextBalloon(QQuickItem *parent)
: QQuickPaintedItem(parent)
, rightAligned(false){}
void TextBalloon::paint(QPainter *painter){
QBrush brush(QColor("#007430"));
painter->setBrush(brush);
painter->setPen(Qt::NoPen);
painter->setRenderHint(QPainter::Antialiasing);
QSizeF itemSize = size();
painter->drawRoundedRect(0, 0, itemSize.width(), itemSize.height() - 10, 10, 10);
if (rightAligned)
{
const QPointF points[3] = {
QPointF(itemSize.width() - 10.0, itemSize.height() - 10.0),
QPointF(itemSize.width() - 20.0, itemSize.height()),
QPointF(itemSize.width() - 30.0, itemSize.height() - 10.0),
};
painter->drawConvexPolygon(points, 3);
}
else
{
const QPointF points[3] = {
QPointF(10.0, itemSize.height() - 10.0),
QPointF(20.0, itemSize.height()),
QPointF(30.0, itemSize.height() - 10.0),
};
painter->drawConvexPolygon(points, 3);
}
}
and here is qml file
ListModel {
id: balloonModel
ListElement {
balloonWidth: 200
}
ListElement {
balloonWidth: 120
}
}
ListView {
anchors.bottom: controls.top
anchors.bottomMargin: 2
anchors.top: parent.top
id: balloonView
delegate: TextBalloon {
anchors.right: index % 2 == 0 ? undefined : parent.right
height: 60
rightAligned: index % 2 == 0 ? false : true
width: balloonWidth
}
model: balloonModel
spacing: 5
width: parent.width
}
Please help with this i really need to see the painted output in qml designer
or please suggest another painting way by which i can see the output in qml designer.
thanks:)

Why does my window not show?

I need to write a GrabWindow, so I derived my class GrabWindow from QQuickWindow:
#include <QtQuickWidgets/QtQuickWidgets>
#include <QString>
class GrabWindow : public QQuickWindow {
Q_OBJECT
public:
explicit GrabWindow(QQuickWindow *parent = nullptr);
public slots:
void capture(QString const &path);
};
// .CPP
#include "grab_window.h"
#include <QImage>
GrabWindow::GrabWindow(QQuickWindow *parent) : QQuickWindow(parent) {
}
void GrabWindow::capture(const QString &path) {
QImage img = this->grabWindow();
img.save(path);
}
After I registered it in QML: qmlRegisterType<GrabWindow>("myapp", 1, 0, "GrabWindow");
And after I defined my window in QML:
import QtQuick 2.4
import QtQuick.Controls 2.2
import QtQuick.Window 2.3
import myapp 1.0
GrabWindow {
id : translationWindow
width : 1024
height : 768
color: "transparent"
visibility: "FullScreen"
visible: true;
signal capture(string path)
MouseArea {
anchors.fill: parent
onClicked: translationWindow.capture("/home/user/saveTest.jpg")
}
}
But it doesn't show on start (I know it's transparent, I mean the grab window doesn't start to show). If instead of GrabWindow I use Window or ApplicationWindow then all works perfectly I am seeing a transparent full-screen window.
What's wrong?
Your GrabWindow is not shown because when you are setting the visible property it's not same as when you use Window's visible property.
Yours is just the visible property of QWindow.
Window does not directly instantiate QQuickWindow, it instantiates a private Qt class QQuickWindowImpl which overrides the visible property with a custom one.
It seems to delay the actual call of the QWindow::setVisible at a later time.
As such, I don't think QQuickWindow is meant to be inherited from. You could try doing visible = true in your Component.onCompleted but I'm not sure it will resolve your problem.
What I would advise you instead is not subclassing QQuickWindow but just creating a new type and pass it the existing Window.
Possible API could be :
Window {
id: myWindow
//...
MouseArea {
anchors.fill: parent
onClicked: WindowGrabber.grab(myWindow, path) //singleton type
}
}
or
Window {
id: myWindow
//...
WindowGrabber { // regular type
id: windowGrabber
window: myWindow
}
MouseArea {
anchors.fill: parent
onClicked: windowGrabber.grab(path) // you could even add a path property in WindowGrabber and not have it as a function parameter if that makes sense for your use case
}
}

How to find out when the QQuickItem-derived class object is initialized?

I've got class that inherits from QQuickItem and inside I'm operating on its width height properties. I'm also exposing my own properties.
class MyQQuickItem : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QUrl source MEMBER m_source NOTIFY sourceChanged)
public:
explicit MyQQuickItem(QQuickItem *a_parent = Q_NULLPTR);
~PDFPage();
signals:
void sourceChanged(const QUrl a_source);
private:
QUrl m_source;
};
In qml:
...
MyQQuickItem
{
width: parent.width / 2
height: parent.height
source: "someSource"
Component.onCompleted
{
console.log("component onCompleted");
}
}
When sourceChanged is emmited I'm working on width height properties, but with the first emit they are not initialized yet, and Component.onCompleted is called after my NOTIFY signal.
I can check if component is ready before getting width and height by calling isComponentComplete and execute my code when it is, but I can't find out when it happens and the initial value of source is skipped when it is not.
Of course I could create slot 'itemReady' and call it from Component.onCompleted, but I have many QMLs using my class and I will create more in the future, so I don't want to make copy-paste job.
I also do not want to set the source in Component.onCompleted.
Connecting to widthChanged, and heightChanged signals isn't also good idea, because I'm resizing my item very frequently, and I don't want to execute my code then.
Is there any way to get completed signal from C++?
EDIT:
I did as #Mitch said - overriten componentComplete method and inside, called it's base class equavilent. isComponentComplete() returns true, but I'm still getting 0 from width() and height() methods:
void MyQQuickItem::componentComplete()
{
QQuickItem::componentComplete();
if(isComponentComplete())
{
qDebug() << "c++: oncompleted, width,height: " << width() << height(); //gives 0,0
}
}
Actually I figured out that Component.onCompleted in QML prints 0's too:
...
MyQQuickItem
{
width: parent.width / 2
height: parent.height
source: "someSource"
Component.onCompleted
{
console.log("component onCompleted width, height: ", width, height); //it gives 0 too
}
}
#derM Component.onCompleted is not emmited by QQuickItem::componentComplete() directly, because it prints its log after the qDebug in c++ above.
So, although it is ready, properties are not initialized.
EDIT2:
Finally I've solved it. The Window was guilty, because it uses contentItem as the parent of his childrens, and in MyQQuickItem Component.onCompleted width and height of contentItem(his parent) are 0.
When I do so:
Window
{
id: wnd
width: 640
height: 480
Component.onCompleted:
{
console.log("Window completed: ", this, width, height);
console.log("windowContentItem: ", contentItem, width, height);
}
MyQQuickItem
{
width: parent.width
height: parent.height
Component.onCompleted:
{
console.log("MyQQuickItem completed: ", this, width, height);
console.log("MyQQuickItem parent: ", parent, parent.width, parent.height);
}
}
}
It prints:
qml: Window completed: QQuickWindowQmlImpl(0x345b5eb4d0) 640 480
qml: windowContentItem: QQuickRootItem(0x345e2cdec0) 640 480
qml: MyQQuickItem completed: MyQQuickItem(0x345b5b99e0) 0 0
qml: MyQQuickItem parent: QQuickRootItem(0x345e2cdec0) 0 0
So MyQQuickItem parent is Window's contentItem. When I replace
width: parent.width
height: parent.height
to:
width: wnd.width
height: wnd.height
It works perfectly, and in my void MyQQuickItem::componentComplete() I've got width and height initialized as expected.
One thing that I do not understand is why in Window onCompleted, contentItem size is correct, but in MyQQuickItem onCompleted the size is 0,0. If anyone could explain it to me, I would be grateful :P
Qt code uses componentComplete() like so:
void QQuickControl::componentComplete()
{
Q_D(QQuickControl);
QQuickItem::componentComplete();
d->resizeContent();
// ...
}
You can do something similar, replacing resizeContent() with your function that works on the width and height. Assuming that function is also called whenever source changes, you'd need the isComponentComplete() check. With this approach, you shouldn't have any issues.
You can inherit QQmlParserStatus and implement componentComplete().
class MyQQuickItem : public QQuickItem, public QQmlParserStatus
{
Q_OBJECT
Q_PROPERTY(QUrl source MEMBER m_source NOTIFY sourceChanged)
public:
explicit MyQQuickItem(QQuickItem *a_parent = Q_NULLPTR);
~PDFPage();
void classBegin();
void componentComplete();
signals:
void sourceChanged(const QUrl a_source);
private:
QUrl m_source;
};
componentComplete() will be called automatically by the QML-engine.

QtQuick: create component from C++

I have a problem with QQuickView instantiating components from C++... Here is my code:
Class definition (vviewerqml.h):
class VViewerQml : public QObject
{
Q_OBJECT
public:
explicit VViewerQml(QSettings &systemSettings, QObject *parent = 0);
~VViewerQml();
protected slots:
void onViewStatusChanged(QQuickView::Status status);
protected:
QString _qmlFolder;
QQuickView _view;
};
Class implementation (vviewerqml.cpp):
#include "vviewerqml.h"
VViewerQml::VViewerQml(QSettings &systemSettings, QObject *parent) :
QObject(parent)
{
// Initialize viewer reading from settings file
_qmlFolder = "/my/path/to/qml/files";
// Initialize the source
connect(&_view, SIGNAL(statusChanged(QQuickView::Status)),
this, SLOT(onViewStatusChanged(QQuickView::Status)));
_view.setSource(QUrl::fromLocalFile(QDir(_qmlFolder).filePath("Main.qml")));
// Show the viewer
_view.show();
}
VViewerQml::~VViewerQml()
{
// Close the viewer
_view.close();
}
void VViewerQml::onViewStatusChanged(QQuickView::Status status)
{
if(status == QQuickView::Ready)
{
QQmlComponent *c =
new QQmlComponent(_view.engine(),
QDir(_qmlFolder).filePath("TextLegacy.qml"));
QQuickItem *i = qobject_cast<QQuickItem*>(c->create());
QQmlEngine::setObjectOwnership(i, QQmlEngine::CppOwnership);
i->setParent(_view.rootObject());
i->setVisible(true);
}
}
Here is my Main.qml:
import QtQuick 2.0
Rectangle {
width: 1024
height: 768
color: "#000000"
Text {
x: 0
y: 0
color: "#ffffff"
text: "Main page"
}
}
And here is my TextLegacy.qml:
import QtQuick 2.0
Item {
Rectangle {
x: 0
y: 0
width: 100
height: 50
color: "#ff0000"
}
}
My code works fine until the load of Main.qml: the QML viewer opens on the screen and I can read the text "Main page" (white on black) on my screen... But unluckily I am not able to load the TextLegacy.qml... if I put a breakpoint in onViewStatusChanged function, the execution reaches that point... no visible errors are shown in the debug console... but I am not able to see on the screen the red rectangle provided by TextLegacy.qml...
What am I missing? Can somebody give some help?
Ok, I found the solution on my own: I have confused setParent with setParentItem... The correct code is:
void VViewerQml::onViewStatusChanged(QQuickView::Status status)
{
if(status == QQuickView::Ready)
{
QQmlComponent *c = new QQmlComponent(_view.engine(),
QUrl::fromLocalFile(QDir(_qmlFolder).filePath("TextLegacy.qml")));
QQuickItem *i = qobject_cast<QQuickItem*>(c->create());
QQmlEngine::setObjectOwnership(i, QQmlEngine::CppOwnership);
i->setParent(this);
i->setVisible(true);
i->setParentItem(_view.rootObject());
}
}
Actually i->setParent(this); defines the parent of i as a QObject (for deleting purposes, for example) while i->setParentItem(_view.rootObject()); actually adds the object to the scene, as a child of the scene root object.