BB10 - Change QML Header Title from C++ - c++

I'm pretty new to C++ and QML, so sort of trying to teach myself the way! I am trying to change the "title" property of a QML Header object. I am currently developing my app off the back off the pushCollector cascades example on GitHub.
I've read elsewhere that I can assign a property alias and do it that way;
NavigationPane {
id: navPane
property alias connectionText:connectionStatus.title
Page {
Container {
Header {
id: connectionStatus
title: "Connection Status:"
verticalAlignment: VerticalAlignment.Bottom
bottomMargin: 0.0
topMargin: 0.0
visible: true
subtitle: ""
}
My question is, how do I make a void function that can dynamically change the title whenever I call the function. I need something like;
void App:changeConnectionText(const QString new Text)
{
//change object title in QML
}
Thanks!

Add this in .cpp
#include <bb/cascades/Header>
You access like this:
void ApplicationUI::changeConnectionText(QString Text) {
Header* myheader = Application::instance()->scene()->findChild<Header*>("connectionStatus");
myheader->setTitle(Text);
}
And you call it like this (in cpp):
changeConnectionText("Yay, connected!");
And QML
Header {
id: connectionStatus
objectName: "connectionStatus" //add this!!!
title: "Connection Status:"
verticalAlignment: VerticalAlignment.Bottom
bottomMargin: 0.0
topMargin: 0.0
visible: true
subtitle: ""
}
Tested, it works

Related

QML: How to retrieve a default font object?

I have a QML Item with some Text fields in it, which should have all the same font. Do achieve this i introduce a new property myFont of type font. Do initialize this property I use the Qt.font function, which creates a font object. But I have to specify at least one property (either family or pointSize).
My Question is now: How can I retrieve the default font for the myFont property?
If I create only a Text{} item, it has already a default font attached, how can I get the same font for the myFont property? (Meanwhile I use a hidden Text field and create an alias to it's font property, but I want a better solution).
Item {
property font myFont: Qt.font({pointSize: 10})
Text {
id: header
font: myFont
text: "My Header"
}
Text {
id: subject
font: myFont
text: "My Subject"
}
Text {
id: message
font: myFont
text: "Some meassage!"
}
}
I think the right way to solve this is to create your own object type with the font you need to use.
In the following example, the new object would be MyText.qml. I don't know your whole code, but I suppose you have a ListView with the delegate you posted in your question.
MyText.qml
import QtQuick 2.0
Text {
font: Qt.font({pointSize: 10})
}
main.qml
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
visible: true
ListModel {
id: myModel
ListElement {
header: "header xxx"
subject: "subject xxx"
message: "message xxx"
}
ListElement {
header: "header yyy"
subject: "subject yyy"
message: "message yyy"
}
ListElement {
header: "header zzz"
subject: "subject zzz"
message: "message zzz"
}
}
ListView {
anchors.fill: parent
model: myModel
delegate: Item {
width: 300; height: 80
Column {
MyText {
id: myheader
text: header + " - family: " + font.family + " size: " + font.pointSize
}
MyText {
id: mysubject
text: subject + " - family: " + font.family + " size: " + font.pointSize
}
MyText {
id: mymessage
text: message + " - family: " + font.family + " size: " + font.pointSize
}
}
}
}
}
Now, I've digged into Qt source code.
And it turns out that Qt uses a registered private TextSingleton which is defined (Qt 5.6) as:
pragma Singleton
import QtQuick 2.2
Text {
}
The font property of various qml controls is initialized:
font: TextSingleton.font
Digging further into C++ code reveals that for the Text item, the font property is a default initialized QFont, which gives the QFont object retrieved from QGuiApplication::font().
As I mentioned here, FontMetrics is the way to go since it's configurable and without using Qt.font(). You can declare it in your parent item or in a SIngleton type and the you can bind the property to it.
Here there's an example
Item {
id: root
FontMetrics {
id: fontMetrics
font.family: "Arial"
font.pixelSize: 24
}
property alias font: fontMetrics.font
Text { font: root.font }
Text { font: root.font }
}
You can access the default font in QML with Qt.application.font.
You can get and set this font in C++ using
QFont font = QApplication::font();
font.setPointSize(12); //make various other changes or get a completely new QFont
QApplication::setFont(font);

Binding C++ to QML code when using Components

---edited url and changed dynamic part to something compilable----
(Using Qt 5.3)
I tried to create a compact sample, but its still too big to post all the files here separately, so i added a link to "uploaded.to" as i cannot seem to attach a zip file here :-((
(warning, spam links and / or waiting time, any better fileshare site you recommend ?
Here is a link to "bindtest.zip" via uploaded.com, beware of spam/ugly pix:
http://ul.to/lqemy5jx
Okay, i will try to post the essence of the files here anyways:
I tried to create a simple Class in C++ containing a StringList and an index.
I Instantiated two Objects of this Class and exposed them via "setContextProperty"
This should be used in QML to initialize a ListView and to be in sync with it.
So whenever a User changes the index in QML, C++ should be notified AND vice versa.
So when i create two Component qml files using the hardwired names set in "setContextProperty" it seems to work fine.
But for the life of me i cannot create a single component file and pass the DataObject to it as a parameter, i simply do not know how to do it, although i tried.
My "final" target ist to create a QML Object dynamically and pass the DataObject to it, this does not work either :-(
So here it comes, code snippets of my sample Project:
Declaring my oh-so-simple Class (DataObject.h)
#ifndef DATAOBJECT_H
#define DATAOBJECT_H
#include <QObject>
#include <QDebug>
class DataObject : public QObject
{
Q_OBJECT
Q_PROPERTY( int index MEMBER m_index NOTIFY indexChanged )
public slots:
int count() const { return m_Elements.count(); }
QString at(int idx) const { return m_Elements.at(idx); }
public:
void setIndex(int theInt) { m_index = theInt; }
signals:
void indexChanged(int);
public: // too lazy to write accessors for this sample, so make it public
QStringList m_Elements;
private:
int m_index;
};
#endif // DATAOBJECT_H
Registering it in main.cpp:
qmlRegisterType<DataObject>("bindtestTypes", 1, 0, "DataObject");
Here is the part of "dialog.cpp" that initializes and exposes two DataObects:
//preparing first list
m_firstDO.m_Elements = QStringList() << "A" << "B" << "C" << "D";
m_firstDO.setIndex(0);
//preparing second list
m_secondDO.m_Elements = QStringList() << "a" << "b" << "c" << "d";
m_secondDO.setIndex(3);
//publish the 2 Dataobjects
m_engine.rootContext()->setContextProperty( "cppDataList_1", &m_firstDO);
m_engine.rootContext()->setContextProperty( "cppDataList_2", &m_secondDO);
Here is the QML file "ShowLists.qml" that should simply show the 2 ListVies on Top of each other, i commented the 2 NOT working approaches that i would love to work, especially the dynamic one:
import QtQuick 2.2
import QtQuick.Window 2.1
import bindtestTypes 1.0
Window {
visible: true
width: 200
height: 400
Rectangle{
anchors.fill: parent
//dynamic: does not work :-(
// need to click on it to create it
// Rectangle{
// id:upperList
// anchors.top: parent.top;
// anchors.left: parent.left
// width:200
// height:200
// MouseArea{
// anchors.fill: parent
// onClicked: {
// var component = Qt.createComponent("SimpleList.qml");
// var dyncbb = component.createObject(parent, {"theDO": cppDataList_1});
// }
// }
// }
// Rectangle{
// id:lowerList
// anchors.bottom: parent.bottom;
// anchors.left: parent.left
// width:200
// height:200
// MouseArea{
// anchors.fill: parent
// onClicked: {
// var component = Qt.createComponent("SimpleList.qml");
// var dyncbb = component.createObject(parent, {"theDO": cppDataList_2});
// }
// }
// }
//static: would not be my first choice but isnt working anyways...
// SimpleList {
// id:upperList
// property DataObject theDO: cppDataList_1
// anchors.top: parent.top;
// anchors.left: parent.left
// }
// SimpleList {
// id:lowerList
// property DataObject theDO: cppDataList_2
// anchors.bottom: parent.bottom;
// anchors.left: parent.left
// }
//hardwired works, but its not workable for my rather complex project...
SimpleList1 {
id:upperList
anchors.top: parent.top;
anchors.left: parent.left
}
SimpleList2 {
id:lowerList
anchors.bottom: parent.bottom;
anchors.left: parent.left
}
}
}
Here is the first hardwired SimpleList1.qml that works fine, as well as the second:
import QtQuick 2.2
ListView {
id: list_view
width: 200
height: 200
currentIndex: cppDataList_1.index
model: cppDataList_1.count()
delegate: Rectangle {
height: 20
width: 200
Text { text: cppDataList_1.at(index); color: (list_view.currentIndex === index)?"red":"black" }
MouseArea{ anchors.fill: parent; onClicked: list_view.currentIndex = index }
}
onCurrentIndexChanged: cppDataList_1.index = currentIndex;
}
This is the "SimpleList.qml" that i cannot seem to get to work:
import QtQuick 2.2
import bindtestTypes 1.0
Rectangle {
ListView {
id: list_view
property DataObject theDO
width: 200
height: 200
currentIndex: theDO.index
model: theDO.count()
delegate: Rectangle {
height: 20
width: 200
Text { text: theDO.at(index); color: (list_view.currentIndex === index)?"red":"black" }
MouseArea{ anchors.fill: parent; onClicked: list_view.currentIndex = index }
}
onCurrentIndexChanged: theDO.index = currentIndex
}
}
So, can anyone of you help me to get this solved ??
IF you dare to follow the uploaded link and run my sample you can see one more glitch.
It displays 2 Windows, one QQQuickWIndow and a Widget.
In the Widget i can change the indexes as well as in the QML Window.
At first they are in sync but then the QML Window does not get updated anymore by changing the index in the widget, i hope its a glitch and not another general error i made.
Greetings & thanks for any help !
Nils
Argh, i found the problem, i did a very simple mistake:
The property i want to set in the SimpleList Component has to be in the root Object, so instead of this:
Rectangle {
ListView {
id: list_view
property DataObject theDO
...
It has to be done this way:
Rectangle {
property DataObject theDO
ListView {
id: list_view
...
Wow, thats an easy solution for a (seemingly) complex Problem.
Greetings,
Nils

Close Sheet with signal from C++ in Blackberry Cascades

I have a UI for some decryption software that gets invoked from the mail client on an encrypted attachment.
My decryption object emits a signal on successful completion of the decryption :
emit decryptedChanged();
which I pass through my controller object (attached as _encryptedattachmentencryptedattachment to the QML UI:
connect(m_decryptor, SIGNAL(decryptedChanged()), this, SIGNAL(decryptedChanged()));
I have a Sheet which is shown on invocation on an encrypted file: when the UI is initialised:
onCreationCompleted: {
splashscreen.open();
}
(at the end of my TabbedPane, before the attachedObjects where the Sheet is.)
I am trying to get the Sheet to close based on the signal.
Sheet {
id: splashscreen
peekEnabled: false
Page {
Container {
layout: DockLayout {
}
ImageView {
horizontalAlignment: HorizontalAlignment.Fill
verticalAlignment: VerticalAlignment.Fill
imageSource: "asset:///images/background.png"
}
Label {
horizontalAlignment: HorizontalAlignment.Fill
verticalAlignment: VerticalAlignment.Center
text: "Decrypting..."
multiline: true
}
}
}
onCreationCompleted: {
_encryptedattachment.decryptedChanged.connect(splashscreen.onDecryptedChanged());
}
function onDecryptedChanged () {
splashscreen.close();
}
}
The splashscreen will not close. I know the object can be seen by the UI, as I use other properties etc. Am I missing a QPROPERTY or something?
Update:
This is my signal definition:
Q_INVOKABLE void decryptedChanged();
Update again:
I have added some console.logs to the QML:
onCreationCompleted: {
_encryptedattachment.decryptedChanged.connect( splashscreen.onDecryptedChanged() );
console.log("connected");
}
function onDecryptedChanged() {
console.log("closing");
splashscreen.close();
}
This gives me the following ouptut:
closing
connected
which is backwards, and the splashscreen does not close.
The problem is in this line:
_encryptedattachment.decryptedChanged.connect( splashscreen.onDecryptedChanged() );
the parentheses after the onDecryptedChanged mean that that function is called, not connected to.
_encryptedattachment.decryptedChanged.connect( splashscreen.onDecryptedChanged );
works fine.

qml + master-detail

I want to use qml with master-detail interface, but i don't know how to pass current item to detail view right way. The ListView in master view uses C++ model (add-on of QSQLTableModel, it's work fine) and I see two ways to pass item:
Create C++ classes with fields with static name like QSqlRecord field names and pass it to qml with w->rootContext()->setContextProperty() (w is QDeclarativeView *), but now i don't use any classes like this and can change my database and qml views without changing c++ code, I would like to save it
Create a lot of properties in any detail qml like
Rectangle {
id: mainRect
property alias myFieldName: txt_nominal.text
Column {
width: parent.width
Text {
id: txt_nominal
font.bold: true
}
}
}
and set this properties from c++ code by setting w->rootContext()->setContextProperty(record.fieldName(i),record.field(i).value()); (record - QSqlRecort at current row)
Is there any easier way to solve my problem?
PS The code I wrote above is not checked for accuracy, and is written to make it more clear what I mean
UPD
Maybe it will be useful for somebody, I discovered 3-rd way, rather, the modification of second - you can wrap fields into QVariantMap and pass only one object to qml. This is exactly what I wanted
in cpp:
QVariantMap testObject;
testObject["testField"]="first string from cpp";
testObject["testField2"]="second string from cpp";
rootContext()->setContextProperty("testObject",testObject);
in qml:
Text {
id: simpleTextt
text: testObject.testField
anchors.centerIn: parent
}
You could use the isCurrentItem property of the delegate to pass the data from ListView delegate to your details qml. That way you could get away without have to add additional c++ code. This is basically your second approach but without c++. You also do not need to add many properties as long as each of your QML elements that you want to change have an id.
If you have a number of different QML for different details views you would also have to use the Loader to load the appropriate details QML.
Just a toy example assuming that you have only one details template for all of your elements in the list (as mentioned above if that is not the case than you can use loader instead of detailsRect):
Rectangle {
width: 300; height: 400
Rectangle {
id: detailsRect
anchors.right: parent.right
width: 100
height: 500
color: "blue"
Text {
id: detailsText
text: ""
}
}
ListView {
id: list
anchors.fill: parent
model: 20
delegate: Rectangle {
color: ListView.isCurrentItem ? "red" : "green"
width: 40
height: 40
Text {
text: index
}
ListView.onIsCurrentItemChanged: {
if(ListView.isCurrentItem)
{
detailsRect.color = "yellow"
detailsText.text = index
}
}
MouseArea {
anchors.fill: parent
onClicked: {
list.currentIndex = index
}
}
}
}
}

Specifying font for many Text-elements in Qt QML

I have a widget specified through a QML file. This widget contains a top levelRectangle which contains two Columns. Each of these Columns contains many Text-elements. This QML widget is wrapped in a subclass of QDeclarativeView in C++.
I want to specify the font for each of these Text-elements. Today I do this by specifying top-level properties:
property string fontfamily: "Arial"
property bool fontbold: false
property bool fontitalic: false
property int fontpixelsize: 11
property string fontcolor: "White"
and bind each Text-elements to these properties:
Text
{
color: fontcolor
font.family: fontfamily
font.bold: fontbold
font.italic: fontitalic
font.pixelSize: fontpixelsize
...
}
This isn't very elegant and new fields needs to be added every time I need support for something new (e.g. underlined fonts). I have not been able to declare a property of type font and bind to this instead (widget is blank and qmlviewer warns about "expected type after property").
Is there a better way to specify a font for all Text-elements?
Note! I'm handwriting the QML files.
In Qt 5.6 (at least, probably earlier too), you can use Qt.font() to dynamically allocate a font object and refer to it elsewhere. So, this works:
property font myFont: Qt.font({
family: fontfamily,
bold: fontbold,
italic: fontitalic,
pixelSize: fontpixelsize
});
Text
{
color: fontcolor
font: parent.myFont
}
More info on Qt.font() here: https://doc.qt.io/qt-5/qml-qtqml-qt.html#font-method
Another possibility is to write a new QML component, that inherits from Text an sets some properties by default:
StyledText.qml
import QtQuick 1.0
Text {
// set default values
color: "blue"
font.family: "Arial"
font.bold: true
font.italic: true
font.pixelSize: 12
}
main.qml
import QtQuick 1.0
Rectangle {
Row {
spacing: 10
Column {
StyledText {
text: "Foo1"
}
StyledText {
text: "Bar1"
}
StyledText {
text: "Baz1"
}
}
Column {
StyledText {
text: "Foo2"
}
StyledText {
text: "Bar2"
}
StyledText {
text: "Baz2"
}
}
}
}
One possible solution is to write a function, that iterates over the children of a passed element (for example a Column). In this function all the properties can be set:
import QtQuick 1.0
Rectangle {
Row {
spacing: 10
Column {
id: col1
Text {
property bool useStyle: true
text: "Foo1"
}
Text {
property bool useStyle: true
text: "Bar1"
}
Text {
property bool useStyle: true
text: "Baz1"
}
}
Column {
id: col2
Text {
property bool useStyle: true
text: "Foo2"
}
Text {
text: "not styled"
}
Text {
property bool useStyle: true
text: "Baz2"
}
}
}
function setTextStyle(parentElement) {
for (var i = 0; i < parentElement.children.length; ++i) {
console.log("t", typeof parentElement.children[i]);
if (parentElement.children[i].useStyle) { // apply style?
parentElement.children[i].color = "blue";
parentElement.children[i].font.family = "Arial"
parentElement.children[i].font.bold = true;
parentElement.children[i].font.italic = true;
parentElement.children[i].font.pixelSize = 12;
}
}
}
// set style
Component.onCompleted: {
setTextStyle(col1);
setTextStyle(col2);
}
}
Each element, that contains the property useStyle that is set to true, gets styled. This is shorter, than assigning the style manually, but you can still define which elements should get styled or not.
Necro posting, but I feel it's still missing an up-to-date solution.
FontMetrics will do the trick without using Qt.font(). You can declare it in your parent item or in a Singleton type, and then you can bind the property to it.
Here there's an example
Item {
id: root
FontMetrics {
id: fontMetrics
font.family: "Arial"
font.pixelSize: 24
}
property alias font: fontMetrics.font
Text { font: root.font }
Text { font: root.font }
}
Some useful workarounds here, but I'm stuck being able to define some base fonts while still being able to specify details later, for more than a few fonts. In particular, because:
FontLoader sets the same name for every font of the same family. Ref
If Qt.font() (or FontMetrics) is used, it's all or nothing for the font property.
Defining new Text Components requires one file per font. Update: maybe not true since 5.15.
Triggering a function is difficult to manage effectively across a whole application.
I think there's room for one more solution:
In my main.qml I used one FontLoader per font file, but only bothered setting an id when the family changes:
FontLoader { source: "qrc:/fonts/ITCAvantGardeStd-Bk.otf"; id: flAvantGarde; }
FontLoader { source: "qrc:/fonts/ITCAvantGardeStd-BkObl.otf"; }
FontLoader { source: "qrc:/fonts/ITCAvantGardeStd-BoldCn.otf"; }
FontLoader { source: "qrc:/fonts/ITCAvantGardeStd-Md.otf"; }
FontLoader { source: "qrc:/fonts/ADAM.CG PRO.otf"; id: flAdam; }
and then, and here's the significant part, defined one property var per base font like so:
property var fontAgBk: { "family": flAvantGarde.name, "styleName": "Book" }
property var fontAgBkObl: { "family": flAvantGarde.name, "styleName": "Book Oblique" }
property var fontAgBoldCn: { "family": flAvantGarde.name, "styleName": "Bold Condensed" }
property var fontAgMd: { "family": flAvantGarde.name, "styleName": "Medium" }
property var fontAdam: { "family": flAdam.name }
The var is key being able to specify a dictionary that can be defined once, but pulled apart later.
Elsewhere in any qml, I can do something like:
Text {
id: myText
color: "#123456"
font.family: fontBoldCn.family
font.styleName: fontBoldCn.styleName
font.pixelSize: 24
font.letterSpacing: 5
}
Still requires some repetitive code, but at least the magic strings are only defined once.