Why does my window not show? - c++

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
}
}

Related

How to set dynamic QFont size?

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.

Qt Create a QML Slider Sensitive to Touch Events

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.

QML: Type Error with custom QObject

I'm trying out something in QML to try and make it alittle easier to merge the two more seamlessly; to be precise, I'm trying to link an object with structured data to QML.
I have the following setup:
main.cpp:
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "dataobject.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
qmlRegisterType<DataObject>("DO", 1,0,"DataObject");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
return app.exec();
}
dataobject.h:
#ifndef DATAOBJECT_H
#define DATAOBJECT_H
#include <QObject>
#include <QVariant>
#include <iostream>
class DataObject : public QObject
{
Q_OBJECT
Q_PROPERTY(qreal a MEMBER a NOTIFY aChanged)
Q_PROPERTY(qreal b MEMBER b NOTIFY bChanged)
public:
explicit DataObject(QObject *parent = 0);
signals:
void aChanged();
void bChanged();
public slots:
void printState() {
using namespace std;
cout << a << ", " << b << endl;
}
private:
qreal a;
qreal b;
};
#endif // DATAOBJECT_H
dataobject.cpp:
#include "dataobject.h"
DataObject::DataObject(QObject *parent) :
QObject(parent)
{
connect(this, SIGNAL(aChanged()), this, SLOT(printState()));
connect(this, SIGNAL(bChanged()), this, SLOT(printState()));
}
main.qml:
import DO 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
menuBar: MenuBar {
Menu {
title: qsTr("File")
MenuItem {
text: qsTr("Exit")
onTriggered: Qt.quit();
}
}
}
Text {
text: qsTr("Hello World")
MouseArea {
property DataObject myData;
anchors.fill: parent
drag.target: parent
onReleased: {
myData.a = mouse.x;
myData.b = mouse.y;
}
}
}
}
qml.qrc:
<RCC>
<qresource prefix="/">
<file>main.qml</file>
</qresource>
</RCC>
Now, what I'd hoped was that values generated by QML could be feed into a object in C++ directly (i.e. the onReleased handler in the MouseArea trying to write to the myData field). However, this basic proof of concept doesn't work, but I don't really understand why.
The error I get (on drag and release of the mouse button) is:
qrc:///main.qml:29:TypeError:Type error
Which matches up with the line "myData.a = mouse.x;", so it fails straight away.
Any idea's where I'm going wrong? I've tried with the fields being int, double, QVariant, and qreal, none of which have worked. Is in a fundamental inability in QML to link objects like that? If so, any idea how, for example, anchors.fill is implemented in the Qt source code?
It helps if you break down the expression on the line where the error is coming from. Try just printing myData.a first:
print(myData.a)
myData.a = mouse.x;
myData.b = mouse.y;
qrc:///main.qml:31: TypeError: Cannot read property 'a' of null
So, myData is null. We can verify this with another QObject-based type:
MouseArea {
property DataObject myData;
property Item item
anchors.fill: parent
drag.target: parent
onReleased: {
print(item)
myData.a = mouse.x;
myData.b = mouse.y;
}
}
qml: null
So, you can fix this error by initialising the property:
property DataObject myData: DataObject {}
You can think of QObject-based properties as pointers for JavaScript; they can be null or point to a valid object... or be undefined. :) I can't find anything mentioned about this here, but that's where this behaviour should be mentioned.
If you'd like to simplify things, you can have the object default-constructed for you by making it a child object of the MouseArea, rather than a property:
MouseArea {
DataObject {
id: myData
}
anchors.fill: parent
drag.target: parent
onReleased: {
myData.a = mouse.x;
myData.b = mouse.y;
}
}
Currently, you won't be able to refer to this property from C++. However, you can achieve this in two ways:
Declare a property alias to the item.
Give it an objectName and use QObject::findChild to find it.

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.

QML c++ signal & slots

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.