Using a method in the created Window in QT - c++

Right now, there is a button for activating the function, like:
Window {
id: window
width: Screen.width
height: Screen.height
visible: true
property alias originalImage: originalImage
title: qsTr("Window1")
Button{
id: startButton
x: 50
y:100
text: "Open"
onClicked: {
VideoStreamer.openVideoCamera()
}
}
But I want to activate this method just after the window is created, not with a button click. Like
Window {
id: window
width: Screen.width
height: Screen.height
visible: true
property alias originalImage: originalImage
title: qsTr("Window1")
VideoStreamer.openVideoCamera()
}
But it doesn't work like that. I get expected token ":" error. It expects something like somecondition: How am I going to manage this without a button, or without an external user-needed condition like onclicked:, etc. ?

You can use Component.onCompleted signal to do things right after component is created.
The onCompleted signal handler can be declared on any object. The order of running the handlers is undefined.
Window {
id: window
width: Screen.width
height: Screen.height
visible: true
property alias originalImage: originalImage
title: qsTr("Window1")
Component.onCompleted:
{
VideoStreamer.openVideoCamera()
}
}
P.S.: if your VideoStreamer is an QML object maybe it is a better to use this signal directly there.

Related

C++ [QT 5.15.2] : virtual keyboard shift button is disabled until I click on my textField

I'm developping an app using QT 5.15 LTS (5.15.2). I have the following QML item that I use to handle virtual keyboard interactions :
//InputScreen.qml
import QtQuick 2.3
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.2
import QtQuick.VirtualKeyboard 2.3
Rectangle
{
id: inputScreen
property var target: undefined
width: app.width
height: app.height
color: "#44000000"
z: 200
onVisibleChanged:
{
if (inputScreen.visible == true)
{
fld.text = target.text;
inputPanel.forceActiveFocus();
fld.forceActiveFocus();
fld.clicked(); //Tried to force a click here. Explanation below
}
}
Rectangle
{
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
height: 40
width: 600
z: 201
TextField
{
id: fld
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: 400
height: 40
font.pixelSize: 24
focus: true
}
Button
{
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
width: 200
height: 40
font.pixelSize: 24
text: "Valider"
onClicked:
{
if (target != undefined)
{
target.text = fld.text;
inputScreen.visible = false;
}
}
}
}
InputPanel
{
id: inputPanel
height: 400
width: app.width - 40
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
focus: true
z: 201
}
MouseArea
{
anchors.fill: parent
onClicked:
{
target.text = fld.text;
inputScreen.visible = false;
}
}
}
When my item becomes visible, the textField gets the focus correctly and I can start typing through the InputPanel virtual keyboard. There's however one single problem : the shift key does not work !
In order to make that shift key enable itself, I need to click on the TextField, which beats the purpose of forcing the active focus on it to begin with.
Is there any way to fix that problem ? Or at the very least, is there any kind of workaround I can use to enable that shift key ?
I have already tried to force a click inside my TextField (as seen in the code above), but it didn't work.
For context : here's the content of main.qml as well, in order to give out a working example :
import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Layouts 1.3
import QtQuick.Controls 1.4
import QtQuick.VirtualKeyboard 2.3
Window
{
id: app
visible: true
width: 640
height: 480
title: qsTr("Example")
InputScreen
{
id: inputScreen
visible: false
}
Rectangle
{
id: rect
height: parent.height * 0.8
width: parent.width * 0.8
anchors.centerIn: parent
border.color: "#FF0000"
z: 1
TextField
{
id: myField
anchors.centerIn: parent
width: 200
height: 24
MouseArea
{
anchors.fill: parent
onClicked:
{
inputScreen.target = myField;
inputScreen.visible = true;
}
}
}
}
}
I finally managed to find a trick that enabled that shift key without requiring to click on my already focused TextField, and it's actually very simple.
I simply added the following line in the onClicked event of the MouseArea inside the "myField" TextField :
MouseArea
{
anchors.fill: parent
onClicked:
{
parent.focus = true; //This line solved the issue !
//...
}
}
I have no idea why, but somehow this was enough to actually unlock the shift key from my InputPanel virtual keyboard without requiring me to click a second time on the TextField from my InputScreen item. I suppose it has probably something to do with the fact that my MouseArea is hijacking the focus of the "myField" TextField, which triggers some sort of odd behaviour on the InputPanel item despite having another TextField item focused.
In any case, I hope this will help someone.

Hide a rectangular block temporarily from the main window in QML

I created a nested rectangular block i.e. a rectangle inside a main rectangular block in QML. Now I have to hide the inner rectangular block on some operation and once the operation is finished make it visible again. I am trying the following:
Rectangle {
id: window
width: 450
height: 550
property bool isTopToolBarAreaVisible : true
Rectangle {
id: toolBarArea
width: 1
height: parent.height
color: toolBarColor
visible : isTopToolBarAreaVisible
ToolButton {
contentItem: Text {
text: "Save as"
}
onClicked: {
...
isTopToolBarAreaVisible = false // hide the inner rectangule
window.grabToImage(function(result) {
result.saveToFile(fileName);
}, Qt.size(window.width*2,window.height*2));
isTopToolBarAreaVisible = true // show the inner rectangle again
}
}
}
}
I created a boolean isTopToolBarArea to control the visibility but it does not work. Can anyone help.
This seems to work. I'm hiding the Rectangle containing the ToolButton when onClicked is triggered and show it again inside the callback assigned to grabToImage(callback, targetSize). Adding the RowLayout was just to make the ToolButton horizontally centered in the Rectangle.
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Rectangle {
id: window
anchors.fill: parent
color: "green"
Rectangle {
id: toolBarArea
width: parent.width
height: 40
color: "red"
RowLayout {
anchors.fill: parent
ToolButton {
contentItem: Text { text: "Save as" }
onClicked: {
toolBarArea.visible = false
window.grabToImage(function(result) {
result.saveToFile("test.png")
toolBarArea.visible = true
}, Qt.size(window.width * 2, window.height * 2))
}
}
}
}
}
}
If you're just calling your delayGrab() function inside your onClicked handler, that still won't work. You're delaying the work but not allowing the UI thread to continue running while it's waiting. If your function takes a long time, a worker thread would be preferable. But for fairly short tasks, a timer will work. The timer should look something like this:
Rectangle {
...
Rectangle {
...
ToolButton {
onClicked: {
isTopToolBarAreaVisible = false // hide the inner rectangle
myTimer.start();
}
}
Timer {
id: myTimer
interval: 10 // Adjust this as needed
onTriggered: {
window.grabToImage(function(result) {
result.saveToFile(fileName);
}, Qt.size(window.width*2,window.height*2));
isTopToolBarAreaVisible = true // show the inner rectangle again
}
}
}
}

If-statement in Page{} environment QML

i simply try to decide to display a different page in my QML-Projecet with respect to a certain condition. Right now im doing it like this:
Page {
id: pageMain
visible: true
width: 1024
height: 600
if (condition) {
Page1{}
}
else Page2{}
}
But unfortunately this isnt working as expected. How can i solve my problem?
Thanks in advance :)
You're mixing the syntax of javascript and QML. You probably want to use a Loader like this:
Page {
id: pageMain
visible: true
width: 1024
height: 600
Component {
id: page1
Page1 {}
}
Component {
id: page2
Page2 {}
}
Loader {
sourceComponent: condition ? page1 : page2
}
}

CurrentText property of QML Combo box not changing when read from C++

I am currently using QML/C++ for mobile development and facing a problem that, for sure demonstrates my poor ability to design QML/C++ applications. Hope you can help me here.
This is my main.qml:
import QtQuick 2.12
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.12
ApplicationWindow {
id: window
visible: true
width: 640
height: 480
title: qsTr("(Nome da Aplicação)")
header: ToolBar{
RowLayout{
anchors.fill: parent
ToolButton {
id: toolButton
text: stackView.depth > 1 ? "\u25C0" : "\u2630"
onClicked: drawer.open()
}
Label {
text: stackView.currentItem.title
elide: Label.ElideRight
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
Layout.fillWidth: true
}
}
}
Drawer {
id: drawer
width: window.width * 0.33
height: window.height
Column{
anchors.fill: parent
ItemDelegate {
text: qsTr("Operações")
width: parent.width
onClicked: {
stackView.push("Operacoes.qml")
drawer.close()
}
}
ItemDelegate {
text: qsTr("Produtos")
width: parent.width
onClicked: {
stackView.push("Produtos.qml")
drawer.close()
}
}
ItemDelegate {
text: qsTr("Configurações")
width: parent.width
onClicked: {
stackView.push("Configuracoes.qml")
drawer.close()
}
}
}
}
StackView {
id: stackView
initialItem: "Operacoes.qml"
anchors.fill: parent
}
}
The combo box whose value I need to access from C++ is defined in Operacoes.qml which consists of
import QtQuick 2.12
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
import QtCharts 2.3
Item {
objectName: "janelaResumo"
property alias title: paginaOperacoes.title
property alias primeiroGraf: primeiroGraf
property alias segundoGraf: segundoGraf
property alias terceiroGraf: terceiroGraf
property alias quartoGraf: quartoGraf
property alias combo_periodoFaturacao_ID: combo_periodoFaturacao_ID
Page {
id: paginaOperacoes
anchors.fill: parent
title: "Resumo de Operações"
ScrollView {
anchors.fill: parent
clip: true
GridLayout {
id: grid_BaseLayout
columns: paginaOperacoes.width < 400 ? 1 : 2
rows: paginaOperacoes.width < 400 ? 4 : 2
anchors.fill: parent
ColumnLayout {
Label {
Layout.alignment: Qt.AlignHCenter
text: qsTr("Faturação")
font.bold: true
}
RowLayout {
ChartView {
id: primeiroGraf
width: 350
height: 350
antialiasing: true
PieSeries {
name: "PieSeries"
PieSlice {
value: 13.5
label: "Slice1"
}
PieSlice {
value: 10.9
label: "Slice2"
}
PieSlice {
value: 8.6
label: "Slice3"
}
}
}
ComboBox {
objectName: "combo_periodoFaturacao"
model: ListModel{
ListElement {
text:"7 dias"
}
ListElement {
text:"Mensal"
}
ListElement {
text:"Anual"
}
}
id: combo_periodoFaturacao_ID
}
}
}
// segundo gráfico
ColumnLayout {
Label {
Layout.alignment: Qt.AlignHCenter
text: qsTr("Tesouraria")
font.bold: true
}
ChartView {
id: segundoGraf
width: 350
height: 350
antialiasing: true
PieSeries {
name: "PieSeries"
PieSlice {
value: 13.5
label: "Slice1"
}
PieSlice {
value: 10.9
label: "Slice2"
}
PieSlice {
value: 8.6
label: "Slice3"
}
}
}
}
}
}
}
Then, C++ ClassX class implements a method to load data that should start by reading qml interface values in order to use them as arguments for some future processig.
void classX::loadData(){
if(mDbStatus == true){
QQuickView view;
const QUrl url(QStringLiteral("qrc:/Operacoes.qml"));
view.setSource(url);
QObject *OperacoesObject = view.rootObject();
QObject *comboFaturacao_t = OperacoesObject->findChild<QObject ("combo_periodoFaturacao");
qDebug() << comboFaturacao_t->property("currentText");
No matter what value lives in the combobox combo_periodoFaturacao depending on user selection, I always get the same value(first element of the respective combobox model) in comboFaturacao_t->property("currentText");
I am aware that I must avoid referring explicitly my UI from C++!
I also understand that, for each loadData() call, I am instantiating a new QQuickView object, but how can I simply collect a few UI values to serve as parameters for the execution of loadData() without implement a cpp class "binded" to my fileX.qml?
No matter what value lives in the combobox comboFaturacao depending on user selection, I always get the same value(first element of the combobox model)
Based on the code you posted, and except if I missed something, you are reading the value of "currentText" immediately after creating your view, without waiting for the user to select anything. So this will return the initial value when your view is created.
but how can I simply collect a few UI values to serve as parameters for the execution of loadData() without implement a cpp class "binded" to my fileX.qml
Exposing C++ to the UI is really the way to go, and a good practice, which forces to avoid high level logic to GUI dependencies. Which means not depending on implementation detail (GUI in this case). That said, if this is what you want, you can read properties from C++, but still need to wait for the user to be "done", which can be done by:
Creating the view on the heap instead of the stack and saving it somewhere
Connecting a slot like onLoadDataUserSettingsReady to a QML signal, using connect (probably the older SIGNAL/SLOT syntax to allow connecting to an arbitrary signal)
Return from loadData, as you will need to wait for the user to interact with the UI without blocking the main thread
And whenever you emit your QML signal that says "The user is done", your onLoadDataUserSettingsReady slot will be executed, allowing you to read the QML properties you are interested with (or directly pass them in the signal/slot)
But as you can see, this is a bit complex, and forces you to make loadData asynchronous, which may not be what you want. You could potentially make it synchronous using a thread that's not the main thread, and a QSignalSpy or other to wait for a signal, but again, not a great solution. I would still recommend exposing a C++ instance with setContextProperty, and reading from this object in your loadData method, whenever needed.

grid of MouseArea in Qt QML

I have made an Item in QT QML which contains a MouseArea element.
Here is the code,
import QtQuick 1.0
Rectangle {
id: base
width: 240
height: 320
x:0; y:0
color: "#323138"
/////////////////////// MAIN FOCUSSCOPE //////////////////////
FocusScope {
id: mainfocus
height: base.height; width: base.width
focus: true
/////////////////////// MAIN GRID ///////////////////////////
GridView {
id: maingrid
width: base.width-10; height: base.height-titlebar.height-10
x: 5; y: titlebar.height+5;
cellHeight: maingrid.height/3; cellWidth: maingrid.width/3-1
Component {
id: myicon
Rectangle {
id: wrapper
height: maingrid.cellHeight-10; width: maingrid.cellWidth-10
radius: 8; smooth: true
color: GridView.isCurrentItem ? "#c0d0c0" : "transparent"
focus: true
MouseArea {
id: clickable
anchors.fill: wrapper
hoverEnabled: true
//onClicked: func()
}
Image {
id: iconpic
source: "./ui6.svg"
anchors.centerIn: wrapper
}
Text {
id: iconname
color: wrapper.GridView.isCurrentItem ? "black" : "#c8dbc8"
anchors.top: iconpic.bottom; anchors.horizontalCenter: iconpic.horizontalCenter
text: name
}
}
}
model: 4
delegate: myicon
focus: true
}
}
//////////////////////// TITLEBAR ///////////////////////
Rectangle {
id: titlebar
x:base.x
y:base.y
height: 25; width: base.width
color : "#356f47"
Text {
color: "#fdfdfd"
anchors.centerIn: titlebar
text: "title"
}
}
}
I want to make a grid of such Items so that it gives me a grid of custom made click-able Items that I created which I can use for different functions.
Using the GridView element, I was able to make such a grid which used number of my custom made Items as a template.
The problem is that when I click anyone of these Items it executes a single function since there was only one MouseArea element in my Item. I am able to detect a click on an Item, but not able to uniquely determine which Item was clicked. How do I achieve this ?
Of course, I might be doing it wrong, so other suggestions are also welcome.
Thanks
When the GridView item creates the instances they inherit the index variable. This identifies the unique item.
MouseArea {
id: clickable
anchors.fill: wrapper
hoverEnabled: true
onClicked: {
maingrid.currentIndex=index;
switch (index)
{
case 0:
//Function or method to call when clicked on first item
break;
case 1:
//Function or method to call when clicked on second item
break;
default:
//Function or method to call when clicked on another item
break;
}
}
}
I hope it helps you!