I want to call C++ function from Cascade QML
this is CalcolatorQML.cpp
// Default empty project template
#include "CalcolatorQML.hpp"
#include <bb/cascades/Application>
#include <bb/cascades/QmlDocument>
#include <bb/cascades/AbstractPane>
#include <QDebug>
using namespace bb::cascades;
CalcolatorQML::CalcolatorQML(bb::cascades::Application *app)
: QObject(app)
{
// create scene document from main.qml asset
// set parent to created document to ensure it exists for the whole application lifetime
QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(this);
// create root object for the UI
AbstractPane *root = qml->createRootObject<AbstractPane>();
// set created root object as a scene
app->setScene(root);
//Container *container = root->findChild<Container*>("myContainer");
}
void CalcolatorQML::injectContainer(){
qDebug("NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN");
}
and I declare the function as Q_INVOKABLE void injectContainer();
this is CalcolatorQML.hpp
// Default empty project template
#ifndef CalcolatorQML_HPP_
#define CalcolatorQML_HPP_
#include <QObject>
namespace bb { namespace cascades { class Application; }}
/*!
* #brief Application pane object
*
*Use this object to create and init app UI, to create context objects, to register the new meta types etc.
*/
class CalcolatorQML : public QObject
{
Q_OBJECT
public:
CalcolatorQML(bb::cascades::Application *app);
virtual ~CalcolatorQML() {}
Q_INVOKABLE void injectContainer();
};
#endif /* CalcolatorQML_HPP_ */
and this is my main.qml, you can see in b1 click this statement injection.injectContainer1();
import bb.cascades 1.0
Page {
content: Container {
Container {
layout: StackLayout {
}
TextField {
id: textFieldId
}
Container {
layout: StackLayout {
orientation: LayoutOrientation.LeftToRight
}
verticalAlignment: VerticalAlignment.Center
horizontalAlignment: HorizontalAlignment.Center
Button {
id: b1
text: "1"
onClicked: {
injection.injectContainer();
}
}
Button {
id: b2
text: "2"
}
Button {
id: b3
text: "3"
}
}
Container {
layout: StackLayout {
orientation: LayoutOrientation.LeftToRight
}
verticalAlignment: VerticalAlignment.Center
horizontalAlignment: HorizontalAlignment.Center
Button {
id: b4
text: "4"
}
Button {
id: b5
text: "5"
}
Button {
id: b6
text: "6"
}
}
Container {
layout: StackLayout {
orientation: LayoutOrientation.LeftToRight
}
verticalAlignment: VerticalAlignment.Center
horizontalAlignment: HorizontalAlignment.Center
Button {
id: b7
text: "7"
}
Button {
id: b8
text: "8"
}
Button {
id: b9
text: "9"
}
}
Container {
layout: StackLayout {
orientation: LayoutOrientation.LeftToRight
}
verticalAlignment: VerticalAlignment.Center
horizontalAlignment: HorizontalAlignment.Center
Button {
id: b0
text: "0"
}
}
Container {
layout: StackLayout {
orientation: LayoutOrientation.LeftToRight
}
verticalAlignment: VerticalAlignment.Center
horizontalAlignment: HorizontalAlignment.Center
Button {
id: bPlus
text: "+"
}
Button {
id: bMult
text: "*"
}
Button {
id: bEqual
text: "="
}
Button {
id: bDivide
text: "/"
}
Button {
id: bMinus
text: "-"
}
}
}
}
}// end of Page
My problem is the function injectContainer not invoked
Thanks
In CalcolatorQML.cpp, before the line:
AbstractPane *root = qml->createRootObject<AbstractPane>();
add:
qml->setContextProperty("app", this);
Then within onClicked in your QML, try:
app.injectContainer();
try this one
AbstractPane *root = qml->createRootObject<AbstractPane>();
qml->setContextProperty("root", this);
In the Qml onClick write
root.injectContainer();
Related
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 need to call a C++ class method with parameters from UI after animation of SwipeView ends.
main.ui
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
SwipeView {
id: swipeView
anchors.fill: parent
Page1 {
id: page1
}
Page2{
id: page2
}
}
XOR {
id: xor
onXorEnded: {
//swipeView.setCurrentIndex(0)
}
onQChanged: {
page2.bar.value = xor.getq()
}
}
}
Page1Form.ui.qml
Page1Form {
kpButton.onClicked: {
kpDialog.visible = true
}
xorButton.onClicked: {
swipeView.setCurrentIndex(1)
xor.crypt(file_path.text, key_path.text, out_path.text)
}
fpButton.onClicked:{
fpDialog.visible = true
}
FileDialog {
id: fpDialog
onAccepted: {
file_path.text = fpDialog.fileUrl
}
}
FileDialog {
id: kpDialog
onAccepted: {
key_path.text = kpDialog.fileUrl
}
}
}
It seems like in xorButton.onClicked xoring starting before animation of swipe view ends. How it works now: Imgur
As a workaround you can bind your action to index changing:
xorButton.onClicked: {
swipeView.setCurrentIndex(1)
}
SwipeView {
id: swipeView
onCurrentItemChanged: {
if(currentIndex == 1)
xor.crypt(file_path.text, key_path.text, out_path.text)
}
}
But anyway, that fires not at end of animation.
As another workaround you can use StackView. It has more properties to control the animation. Another advantage of this control is that user cannot swipe it when you don't expect it. In your case an user just can swipe that. One more advantage is that page doesn't take memory when you don't need it.
import QtQuick 2.7
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
Window {
visible: true
width: 800
height: 800
StackView {
id: view
anchors.fill: parent
initialItem: page1
onBusyChanged: {
if(!busy && currentItem.objectName == "page2")
currentItem.run();
}
}
Component {
id: page1
Rectangle {
color: "green"
objectName: "page1"
Button {
anchors.centerIn: parent
text: "swipe me"
onClicked:
view.push(page2)
}
}
}
Component {
id: page2
Rectangle {
color: "yellow"
objectName: "page2"
function run() { sign.visible = true; }
Rectangle {
id: sign
anchors.centerIn: parent
width: 100
height: 100
radius: 50
color: "red"
visible: false
}
}
}
}
I have a ListView which has dynamically added Items, and what I kinda want to know is how I could access items for example by index. Specifically, I want to have the color of the item rectangle change when I change the color using the Color Dialog. I guessed it should be maybe possible to first set a variable to the current item before calling the color dialog and then in the onAccepted method change the color of that item using the variable, but I don't know how to achieve anything of this in QML, because I am rather new to QML. Maybe you can offer even a cleaner way to change the color of the item's rectangle when the color dialog was closed (onAccepted). Thx for any help! :)
import QtQuick 2.0
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2
import QtQuick.Controls.Styles 1.4
Rectangle {
id: listViewContainer
width: parent.width/10*9;
height: 50
Behavior on height {
NumberAnimation {
duration: 100;
}
}
gradient: Gradient {
GradientStop {position: 0.0; color: "white" }
GradientStop {position: 1.0; color: "silver" }
}
radius: 5
ColorDialog {
id: colorDialog
title: "Please choose a color"
onAccepted: {
console.log("You chose: " + colorDialog.color)
Qt.quit()
}
onRejected: {
console.log("Canceled")
Qt.quit()
}
}
Component {
id: playerDelegate
Item {
anchors.left: parent.left
anchors.right: parent.right
height: 50
Column {
Text { text: '<b>Name:</b> ' + name }
Row {
MouseArea {
width: 20
height: 20
onClicked: {
colorDialog.visible = true;
playerColor = colorDialog.color;
//open color dialog
}
Rectangle {
radius: 3
anchors.fill: parent
color: playerColor
}
}
}
}
}
}
ListView {
id: playerListView
anchors.fill: parent
model:
ListModel {
id: playerListViewModel;
ListElement {
name: "Bill Smith"
playerColor: "red"
}
}
delegate: playerDelegate
}
Button {
id: addPlayerButton
anchors.top: playerListView.bottom
anchors.left: playerListView.left
anchors.right: playerListView.right
style: ButtonStyle {
label: Text {
text: "Add new player"
horizontalAlignment: Text.Center
}
}
onClicked: {
root.addnewPlayer(playerListView); //dont worry about this method
playerListViewModel.append({name: "Billy", playerColor: "blue"});
listViewContainer.height += 50;
}
}
}
Here is a sure fire way to make a working colorDialog --
in playerDelegate
Component {
id: playerDelegate
Item {
anchors.left: parent.left
anchors.right: parent.right
height: 50
Column {
Text {
text: '<b>Name:</b> ' + name
}
/* Object to store value from color selector */
Item {
id: colorSelector
property color color: "#000000"
onColorChanged: { playerColor = color; }
}
/* box to display color */
Rectangle {
//Layout.fillWidth: true
height: 120
width: 120
anchors.left: button.right
//Layout.alignment: Qt.AlignHCenter
color: colorSelector.color
border.width: 1
border.color: "#000000"
}
/* button to open dialog -- can be mousearea or other clickable object */
Button {
id: button
text: "Browse..."
onClicked: {
colorDialog.color = colorSelector.color
colorDialog.open()
}
}
/* actual color dialog for this delegate */
ColorDialog {
id: colorDialog
modality: Qt.ApplicationModal
title: "Please choose a color"
onAccepted: colorSelector.color = currentColor
}
}
}
}
How do you set the action menu button as selected when it's already on its view? I ave this one view with action menu tabs, then when you tapped one then it'll redirect the user to that view, and I want the button to be displayed as pressed. Where do I set the selected state of the button?
Here's my qml:
import bb.cascades 1.0
Page {
Container {
background: Color.create("#f9f7f2");
layout: StackLayout {}
// Container for holding the title
Container {
horizontalAlignment: HorizontalAlignment.Center
layout: DockLayout {}
ImageView {
horizontalAlignment: HorizontalAlignment.Fill
verticalAlignment: VerticalAlignment.Fill
imageSource: "asset:///images/navigation_bar.png"
}
Container {
horizontalAlignment: HorizontalAlignment.Right
rightPadding: 30
topPadding: 40
layout: DockLayout {}
ImageButton {
id: btnsettings
verticalAlignment: VerticalAlignment.Center
defaultImageSource: "asset:///images/navbar_icon_settings.png"
onClicked: {
// show settings page when the button is clicked
cppObj.onSettingsClicked();
}
}
}
}
Container {
topPadding: 20
leftPadding: 20
rightPadding: 20
bottomPadding: 20
background: Color.create("#F4E9E1");
horizontalAlignment: HorizontalAlignment.Fill
layout: StackLayout {}
Label {
verticalAlignment: VerticalAlignment.Center
horizontalAlignment: HorizontalAlignment.Left
text: cppObj.name
textStyle {
// fontFamily: FontStyle.Default.Myriad
// fontSize: 36
color: Color.create("#60323C")
}
}
}
Container {
verticalAlignment: VerticalAlignment.Center
horizontalAlignment: HorizontalAlignment.Center
layout: DockLayout {}
Divider {}
ScrollView {
scrollViewProperties {
scrollMode: ScrollMode.Vertical
}
/* ImageView {
id: listviewbackground
verticalAlignment: VerticalAlignment.Center
horizontalAlignment: HorizontalAlignment.Center
scalingMethod: ScalingMethod.Fill
imageSource: "asset:///images/list_view_cell.png"
}*/
ListView {
id: lvprojects
dataModel: cppObj.model()
listItemComponents: [
ListItemComponent {
type: "item"
Container {
horizontalAlignment: HorizontalAlignment.Center
layout: DockLayout {}
touchPropagationMode: TouchPropagationMode.Full;
StandardListItem {
title:ListItemData.desc
}
}
}
]
onTriggered: {
var selectedItem = dataModel.data(indexPath);
onClicked: {
// show issue's comment page when the button is clicked
cppObj.onIssueClicked(selectedItem.name);
}
}
}
}
}
}
actions: [
ActionItem {
title: qsTr ("Add Issue")
imageSource: "asset:///images/actionbar_icon_add.png"
ActionBar.placement: ActionBarPlacement.OnBar
onTriggered: {
cppObj.onAddIssuesClicked();
}
},
ActionItem {
title: qsTr ("Issues")
imageSource: "asset:///images/actionbar_icon_issues.png"
ActionBar.placement: ActionBarPlacement.OnBar
onTriggered: {
cppObj.onIssuesClicked();
}
},
ActionItem {
title: qsTr ("Members")
imageSource: "asset:///images/actionbar_icon_members.png"
ActionBar.placement: ActionBarPlacement.OnBar
onTriggered: {
cppObj.onMembersClicked();
}
}
]
}
I guess what you're trying to achieve is to mark a tab as active: void TabbedPane::setActiveTab (bb::cascades::Tab *activeTab)
I want to place the submit button below the colored box. What should I do to fix the layout?
Whenever I adjust the topPadding the whole covered box adjusts not the submit button
import bb.cascades 1.0
Page {
Container {
background: Color.create("#f9f7f2");
layout: StackLayout {}
// Container for holding the title
Container {
horizontalAlignment: HorizontalAlignment.Center
layout: DockLayout {}
ImageView {
horizontalAlignment: HorizontalAlignment.Fill
verticalAlignment: VerticalAlignment.Fill
imageSource: "asset:///images/navigation_bar.png"
}
Container {
horizontalAlignment: HorizontalAlignment.Right
rightPadding: 30
topPadding: 40
layout: DockLayout {}
ImageButton {
id: btnsettings
verticalAlignment: VerticalAlignment.Center
defaultImageSource: "asset:///images/navbar_icon_settings.png"
onClicked: {
// show settings page when the button is clicked
cppObj.onSettingsClicked();
}
}
}
}
Container {
topPadding: 20
leftPadding: 20
rightPadding: 20
bottomPadding: 20
background: Color.create("#F4E9E1");
horizontalAlignment: HorizontalAlignment.Fill
layout: StackLayout {}
Label {
verticalAlignment: VerticalAlignment.Center
horizontalAlignment: HorizontalAlignment.Left
text: "Add Projects"
textStyle {
// fontFamily: FontStyle.Default.Myriad
// fontSize: 36
color: Color.create("#60323C")
}
}
}
ScrollView {
scrollViewProperties {
scrollMode: ScrollMode.Vertical
}
Container {
verticalAlignment: VerticalAlignment.Center
horizontalAlignment: HorizontalAlignment.Center
Divider {}
Container {
rightPadding: 30
leftPadding: rightPadding
bottomPadding: 40
topPadding: 20
verticalAlignment: VerticalAlignment.Bottom
layout: DockLayout {}
ImageView {
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
topMargin: 40
imageSource: "asset:///images/create_project_frame.png"
}
Container {
horizontalAlignment: HorizontalAlignment.Center
leftPadding: 70
rightPadding: 70
topPadding: 60
Label {
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
text: qsTr("Create a Project")
}
}
Container {
leftPadding: 70
rightPadding: 70
topPadding: 150
TextField {
id: tfMemberName
input.submitKey: SubmitKey.Next
topPadding: 50
hintText: qsTr("project name")
}
}
Container {
rightPadding: 30
leftPadding: rightPadding
bottomPadding: 20
verticalAlignment: VerticalAlignment.Bottom
layout: DockLayout {}
ImageView {
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
topMargin: 40
imageSource: "asset:///images/add_member_frame.png"
}
Container {
horizontalAlignment: HorizontalAlignment.Center
leftPadding: 70
rightPadding: 70
topPadding: 60
Label {
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
text: qsTr("Add a Member")
}
}
Container {
leftPadding: 70
rightPadding: 70
topPadding: 150
TextField {
id: tfEmail
input.submitKey: SubmitKey.Next
topPadding: 40
hintText: qsTr("email address")
}
}
Container {
horizontalAlignment: HorizontalAlignment.Left
verticalAlignment: VerticalAlignment.Center
topPadding: 250
leftPadding: 180
layout: DockLayout {}
ImageButton {
id: btnaddmore
defaultImageSource: "asset:///images/button_add.png"
onClicked: {
controller.showProjectsPage(); /*var projectsPage = page.createObject();
navPane.push(projectsPage);*/
}
}
}
Container {
horizontalAlignment: HorizontalAlignment.Right
rightPadding: 250
topPadding: 310
Label {
horizontalAlignment: HorizontalAlignment.Right
text: qsTr("add more")
}
}
}
}
}
ImageButton {
id: btnsubmit
horizontalAlignment: HorizontalAlignment.Center
defaultImageSource: "asset:///images/button_submit.png"
}
}
}
/*actions: [
ActionItem {
title: qsTr ("Add Project")
imageSource: "asset:///images/actionbar_icon_add.png"
ActionBar.placement: ActionBarPlacement.OnBar
/*onTriggered: {
_projects.addProjects()
navigationPane.push(addProjects.createObject())
}
}
]*/
}
I want to achieve this look
If I got you right, this code
import bb.cascades 1.0
Page {
// [0]
Container {
background: Color.Gray
topPadding: 40
leftPadding: 20
rightPadding: leftPadding
bottomPadding: topPadding
layout: StackLayout {
}
// [1]
Container {
leftPadding: 40
rightPadding: leftPadding
background: Color.Red
maxHeight: 350
// [2]
Container {
layoutProperties: StackLayoutProperties {
spaceQuota: 1.5
}
Label {
horizontalAlignment: HorizontalAlignment.Center
text: "Add a Member"
}
TextField {
horizontalAlignment: HorizontalAlignment.Center
hintText: "email address"
}
} // [2]
Container {
layoutProperties: StackLayoutProperties {
spaceQuota: 1
}
horizontalAlignment: HorizontalAlignment.Center
Container {
layout: StackLayout {
orientation: LayoutOrientation.LeftToRight
}
Button {
text: "+"
}
Label {
verticalAlignment: VerticalAlignment.Center
text: "add more"
}
}
}
} //[1]
Button {
horizontalAlignment: HorizontalAlignment.Center
text: "Button submit"
}
} // [0]
}
will give you the following layout:
Simply replace Buttons with your custom ImageButtons