So my objective is to set up themes for an application that I have made. I would like to be able to check a menu option that will change the theme of the application as soon as I press it; and change the theme back to the native one on pressing the button. I understand how to style the Qt Quick Controls but I am having trouble with the loading and unloading of these themes. Currently my application comes with the native-like theme that Qt Controls comes with. I am able to load custom styles but I am unable to swap them the custom styles and the native theme. How would I go about creating this feature?
For example I am trying to swap the theme of a TableView. I first tried setting a property of TableViewStyle to be styleData.value This produced the desired effect of keeping the native-like theme, but now how would I go about assigning the Rectangle QmlObject to a property value? The interpreter complains about how I cannot assign a Object to a property.
TableViewStyle {
property string color: styleData.value
headerDelete: color // This does not work. Because
// styleData.value in undefined
//property string color: rect // Currently doing this does not
//Rectangle {id: rect; color: "red"} // have any effect on the value of
//headerDelegate: color // the headerDelegate. Putting the
// Rectangle in a Component produces
// The same value too.
}
I did not find a way to go back to the default style without having to load the element first with the default style and save it but this works for a Button switching between two custom styles and the default ButtonStyle
// Property used to save the default ButtonStyle
property var defaultStyle: undefined
// Property used to know the current style
property string currentStyle: "blue"
Button {
id: myBtn
text: qsTr("Hello World")
anchors.centerIn: parent
onClicked: {
if(currentStyle == "blue") {
style = myButtonStyleRed;
currentStyle = "red";
} else if(currentStyle == "red") {
style = defaultStyle;
currentStyle = "default";
} else {
style = myButtonStyleBlue;
currentStyle = "blue";
}
}
// Save the default Button style on load before switching to the blue one
Component.onCompleted: {
defaultStyle = myBtn.style;
myBtn.style = myButtonStyleBlue;
}
}
Component {
id: myButtonStyleBlue
ButtonStyle {
label: Text {
color: "blue"
text: control.text
}
}
}
Component {
id: myButtonStyleRed
ButtonStyle {
label: Text {
color: "red"
text: control.text
}
}
}
Related
I'm right now creating my app only in C++, i creating the NavigationPane and adding the container with the Views i need. It works fine, but i want to capture a Button clicked to make the NavigationPane pop the current page and push a diferent (made in runtime) Page.
How can it be achieved, i tried working with the signals, but i think i'm not getting how it works the signals and the QT_SLOTS, in the case of the NavigationPane, it doesn't have those methods as QT_SLOT.
Any advice will be appreciated.
You first need to connect the clicked() signal of your Button to the pop() slot of your NavigationPane. It should look like this:
// Connect the button's clicked() signal to the navigation pane's
// pop() slot.
bool connectResult = QObject::connect(myButton,
SIGNAL(clicked()),
myPane,
SLOT(pop()));
// Use the Q_ASSERT() function to test the return value and
// generate a warning message if the signal slot connection
// wasn’t successful.
Q_ASSERT(connectResult);
// Indicate that the variable connectResult isn't used in the
// rest of the app to prevent a compiler warning.
Q_UNUSED(connectResult);
This page about buttons might help you understand how to handle this. To better understand how to connect objects together, you might also want to have a look at a the signals and slots documentation.
You then have to create and push your new page after the pop. To do that, you simply have to connect the popTransitionEnded (bb::cascades::Page *page) slot of your NavigationPane to your custom function that will do the job.
bool connectResult = QObject::connect(myPane,
SIGNAL(popTransitionEnded(bb::cascades::Page*)),
this,
SLOT(createNewPageAndPushIt(bb::cascades::Page*)));
Q_ASSERT(connectResult);
Q_UNUSED(connectResult);
See this page for more details about the usage of NavigationPane to stack pages.
---------------------TRY THIS-------------
Get sample app from my github samples for your query....
https://github.com/svmrajesh/BB-10-Cascades/tree/master/MY%20APPS/stackNavigation
main.qml: (first page)
import bb.cascades 1.0
NavigationPane {
id: navigationPane
backButtonsVisible: false
peekEnabled: false
Page
{
id: rootPage
Container {
background: Color.LightGray
layout: DockLayout {
}
Label {
text: "First page"
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
}
}
actions: [
ActionItem {
title: "Next page"
ActionBar.placement: ActionBarPlacement.OnBar
onTriggered: {
var page = pageDefinition.createObject();
navigationPane.push(page);
}
attachedObjects: ComponentDefinition {
id: pageDefinition
source: "PageTwo.qml"
}
}
]
}
onPopTransitionEnded: {
page.destroy();
}
}
2.second page
import bb.cascades 1.0
Page {
id: pageTwo
Container {
background: Color.Gray
layout: DockLayout {
}
Label {
text: "Second page"
horizontalAlignment: HorizontalAlignment.Center
}
Container {
layout: StackLayout {
}
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
Button {
text: qsTr("Next Page")
imageSource: "asset:///images/picture1thumb.png"
onClicked: {
// show detail page when the button is clicked
var page = getSecondPage();
console.debug("pushing detail " + page)
navigationPane.push(page);
}
property Page secondPage
function getSecondPage() {
if (! secondPage) {
secondPage = secondPageDefinition.createObject();
}
return secondPage;
}
attachedObjects: [
ComponentDefinition {
id: secondPageDefinition
source: "PageTwoOne.qml"
}
]
}
Button {
text: "Previous Page"
onClicked: {
navigationPane.pop();
}
}
}
}
/* ------------- Use this Code If back button visibility is "True"-----------------
paneProperties: NavigationPaneProperties {
backButton: ActionItem {
title: "Back"
// imageSource: "asset:///back.png"
onTriggered: {
navigationPane.pop();
}
}
} */
}
3.last page
import bb.cascades 1.0
Page {
id: pageTwoone
Container {
background: Color.DarkGray
layout: DockLayout {}
Label {
horizontalAlignment: HorizontalAlignment.Center
text: "Last Page"
}
Container {
layout: StackLayout {}
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
Button {
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
text: qsTr("Goto Home Page")
onClicked: {
// show detail page when the button is clicked
navigationPane.navigateTo(rootPage);
}
}
Button {
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
text: qsTr("Goto Back")
onClicked: {
// show detail page when the button is clicked
navigationPane.pop();
}
}
}
}
}
------------ ADD More pages to navigate using this code----------------------------
-------------copy this code and run.. get sample app from above link if needed ------
Have you checked this?
https://developer.blackberry.com/native/reference/cascades/bb__cascades__navigationpane.html
NavigationPane* navigationPane; // Global var to change current Page with push/pop
void initializeNavigationPane()
{
ActionItem* nextAction = ActionItem::create()
.title("Next page")
.onTriggered(this, SLOT(pushPage()));
navigationPane = NavigationPane::create();
QObject::connect(navigationPane, SIGNAL(popTransitionEnded(bb::cascades::Page*)),
this, SLOT(popFinished(bb::cascades::Page*)));
// Put a new page
navigationPane->push(Page::create()
.content(Label::create("First page"))
.addAction(nextAction, ActionBarPlacement::OnBar));
Application::instance()->setScene(navigationPane);
}
void popFinished(bb::cascades::Page* page){
delete page;
}
//You have to connect this method when you want a new Page pushed.
Q_SLOT void pushPage(){
ActionItem* backAction = ActionItem::create()
.title("Previous page")
.imageSource(QUrl("asset:///back.png"))
.onTriggered(navigationPane, SLOT(pop()));
navigationPane->push(Page::create()
.content(Label::create("Second page"))
.paneProperties(NavigationPaneProperties::create()
.backButton(backAction)));
}
Explication:
An instance of the object NavigationPane allows change the current page to others with the push/pop effect (see image):
developer.blackberry.com/native/files/reference/cascades/images/navigation_pane_push_pop.png
You have to inicialice with:
navigationPane = NavigationPane::create();
And tell to the Application you will use this instance to change page:
Application::instance()->setScene(navigationPane);
Now you app got a NavigationPane, but nothing is inside, if you run it, you will get a black screen, to add a page (the principal page - page0) use push:
navigationPane->push(Page::create()
.content(Label::create("First page")));
To add a new Page that It can go back to the page0 we just push use Push again, Remeber include the back button to go back:
navigationPane->push(Page::create()
.content(Label::create("Second page"))
.paneProperties(NavigationPaneProperties::create()
.backButton(ActionItem::create()
.title("Previous page")
.imageSource(QUrl("asset:///back.png")) //You should add manually this image.
.onTriggered(navigationPane, SLOT(pop()))));
Q_INVOKABLE void insert (intindex, bb::cascades::Page *page )
https://developer.blackberry.com/native/reference/cascades/bb__cascades__NavigationPane.html#function-insert-index-page
Inserts a page at a specified index in the NavigationPane.
The page that is passed must not be 0 or it will be ignored. If the
page is already present in the navigation stack, the operation will
fail. This operation will not trigger a transition effect, even if the
page is added to the top of the stack. If a transition effect is
desired, use push() instead. The topChanged() signal will be emitted
if the operation affects the top node.
Parameters
1- index
The index where the page will be placed. If the index < 0 the the page is inserted in the bottom. If the index > the number of pages in the navigation stack, it is added on top of the stack.
2- page
The page to be inserted, must not be 0.
Since: BlackBerry 10.0.0
An idea is
You could use:
navigationPane.count() To get the current pages in the nagationPane stack, and use:
navigationPane.insert(navigationPane.count()-1, myPageToBeBack); To push a page between the current page and
the previous one
I have one navigation pane and I disabled the back button in the main.qml, however I want to display the back button again to some part of the app. How do i implement this? here's my cpp
pane = qml->createRootObject<NavigationPane>();
// Set created root object as the application scene
app->setScene(pane);
qml->setContextProperty("cppObj", this);
}
void ApplicationUI::onLoginClicked() {
// create scene document from buttonclicked.qml asset
// set parent to created document to ensure it exists for the whole application lifetime
QmlDocument *qml = QmlDocument::create("asset:///projects.qml").parent(this);
qml->setContextProperty("cppObj", this);
Page* root = qml->createRootObject<Page>();
pane->push(root);
}
void ApplicationUI::onAddClicked() {
// create scene document from buttonclicked.qml asset
// set parent to created document to ensure it exists for the whole application lifetime
QmlDocument *qml = QmlDocument::create("asset:///addprojects.qml").parent(this);
Page* root = qml->createRootObject<Page>();
pane->push(root);
}
and here's my main where I disabled the back button
NavigationPane {
backButtonsVisible: false }
How do i make the back button active to some section of the app?
Let's say you have the following code:
StartPage
import bb.cascades 1.0
NavigationPane {
id: navPane
Page {
Container {
horizontalAlignment: HorizontalAlignment.Fill
verticalAlignment: VerticalAlignment.Fill
layout: DockLayout {
}
Button {
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
text: "Next Page"
onClicked: {
var nextPage = page.createObject();
navPane.push(nextPage);
}
}
}
}
attachedObjects: [
ComponentDefinition {
id: page
source: "NextPage.qml"
}
]
}
NextPage:
import bb.cascades 1.0
Page {
// disables backButton (not peeking)
paneProperties: NavigationPaneProperties {
backButton: ActionItem {
enabled: false
}
}
// [0]
Container {
horizontalAlignment: HorizontalAlignment.Fill
verticalAlignment: VerticalAlignment.Fill
layout: DockLayout {
}
Button {
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
text: "Prev page"
onClicked: {
var page = navPane.pop();
if (page)
page.destroy();
}
}
} // [0]
// hides navigation bar
onCreationCompleted: {
navPane.backButtonsVisible = false;
}
}
Then you can completely disable backButton with this code in NextPage.qml
onCreationCompleted: {
navPane.backButtonsVisible = false;
}
Alternatively, if you want to have navigation pane visible but just Back Button disabled, use following in the same NextPage.qml file:
paneProperties: NavigationPaneProperties {
backButton: ActionItem {
enabled: false
}
}
Also, don't forget about peekEnabled property. Using peek BB10 feature user can move between sibling pages using sliding finger movement not touching any buttons on the screen.
It could be done in similar way:
onCreationCompleted: {
navPane.peekEnabled = false;
}
Hope it helps.
I have a QML textInput element like this:
TextBox.qml
FocusScope {
id: focusScope
property int fontSize: focusScope.height -30
property int textBoxWidth: parent.width * 0.8
property int textBoxHeight: 45
property string placeHolder: 'Type something...'
property bool isUserInTheMiddleOfEntringText: false
width: textBoxWidth
height: textBoxHeight
Rectangle {
width: parent.width
height: parent.height
border.color:'blue'
border.width: 3
radius: 0
MouseArea {
anchors.fill: parent
onClicked: {
focusScope.focus = true
textInput.openSoftwareInputPanel()
}
}
}
Text {
id: typeSomething
anchors.fill: parent; anchors.rightMargin: 8
verticalAlignment: Text.AlignVCenter
text: placeHolder
color: 'red'
font.italic: true
font.pointSize: fontSize
MouseArea {
anchors.fill: parent
onClicked: {
focusScope.focus = true
textInput.openSoftwareInputPanel()
}
}
}
MouseArea {
anchors.fill: parent
onClicked: {
focusScope.focus = true
textInput.openSoftwareInputPanel()
}
}
TextInput {
id: textInput
anchors {
right: parent.right
rightMargin: 8
left: clear.right
leftMargin: 8
verticalCenter: parent.verticalCenter
}
focus: true
selectByMouse: true
font.pointSize: fontSize
}
Text {
id: clear
text: '\u2717'
color: 'yellow'
font.pointSize: 25
opacity: 0
visible: readOnlyTextBox ? false : true
anchors {
left: parent.left
leftMargin: 8
verticalCenter: parent.verticalCenter
}
MouseArea {
anchors.fill: parent
onClicked: {
textInput.text = ''
focusScope.focus = true;
textInput.openSoftwareInputPanel()
}
}
}
states: State {
name: 'hasText'; when: textInput.text != ''
PropertyChanges {
target: typeSomething
opacity: 0
}
PropertyChanges {
target: clear
opacity: 0.5
}
}
transitions: [
Transition {
from: ''; to: 'hasText'
NumberAnimation {
exclude: typeSomething
properties: 'opacity'
}
},
Transition {
from: 'hasText'; to: ''
NumberAnimation {
properties: 'opacity'
}
}
]
}
I want to add autocomplete and suggestions like google search to this text box. Autocomple get data from database and database return a list of dictionaries by a pyside SLOT.(or c++ slot)
How I can do this work?
Take a look at this code: https://github.com/jturcotte/liquid/blob/master/qml/content/SuggestionBox.qml
I bet it will do the job.
Edit:
Code that linked above is somewhat complicated and requires C++ backend, so I simplified it and made pure Qml example application, that you can play with, edit a little and apply to your needs. Sources can be found here. Most important things there are:
This implementation of SuggestionBox that uses some sort of model as it's source for completing/suggesting something
Its signal itemSelected(item) will be emitted every time user clicks on item
Main component of application that binds its LineEdit component to SuggestionBox
Note that code is quite rough and written for a sake of example.
I was looking for something very similar: a QML autocomplete component built around QML TextField, rather than the lower-level, more flexible but also more work intensive TextInput as in the question.
Since I could not find that, I implemented it. If anyone wants to use it: it's licensed under MIT and available as part of an application I am developing. You find the component in src/qml/AutoComplete.qml, and the application may serve as usage example. Features:
highlighting of autocompleted characters in bold, as in Google Search
Key bindings (navigating with arrow keys, Return / Enter, Esc to close completion box, Esc Esc to unfocus)
uses a simple QStringList as model for now, with the application showing how to update the model with live SQL database queries when the next key is pressed
heavily documented code, so it should be easy enough to adapt
Let me know if this is useful, I might then package it as a Qt QPM package or even try to make it mature enough to be added to the QML UI library KDE Kirigami.
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
}
}
}
}
}
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.