Customize or Define new Qml ChartView theme - qt

There's an official sample code that customize background and plot area brushes of QChart: Custom chart example
How can I acheive this in Qml way with ChartView(Qml type)?
I want to customize Qml ChartView like that sample. but there's no direct way(qml property) to change brushes from qml. also ChartView.ChartTheme property are predefined and hardcoded in C++. I don't know if its possible and how to inherit QChart to change those properties from C++ and define custom QML Type.

Not all modifiable properties in Qt Charts C ++ are modifiable in QML, although it is possible to access the QChart through filtering using findChild and items.
It is also possible to modify some unexposed axis properties in a similar way.
#include <QtQuick>
#include <QtWidgets>
#include <QtCharts>
class Helper: public QObject
{
Q_OBJECT
public:
Q_INVOKABLE void update_chart(QQuickItem *item){
if(QGraphicsScene *scene = item->findChild<QGraphicsScene *>()){
for(QGraphicsItem *it : scene->items()){
if(QtCharts::QChart *chart = dynamic_cast<QtCharts::QChart *>(it)){
// Customize chart background
QLinearGradient backgroundGradient;
backgroundGradient.setStart(QPointF(0, 0));
backgroundGradient.setFinalStop(QPointF(0, 1));
backgroundGradient.setColorAt(0.0, QRgb(0xd2d0d1));
backgroundGradient.setColorAt(1.0, QRgb(0x4c4547));
backgroundGradient.setCoordinateMode(QGradient::ObjectBoundingMode);
chart->setBackgroundBrush(backgroundGradient);
// Customize plot area background
QLinearGradient plotAreaGradient;
plotAreaGradient.setStart(QPointF(0, 1));
plotAreaGradient.setFinalStop(QPointF(1, 0));
plotAreaGradient.setColorAt(0.0, QRgb(0x555555));
plotAreaGradient.setColorAt(1.0, QRgb(0x55aa55));
plotAreaGradient.setCoordinateMode(QGradient::ObjectBoundingMode);
chart->setPlotAreaBackgroundBrush(plotAreaGradient);
chart->setPlotAreaBackgroundVisible(true);
}
}
}
}
Q_INVOKABLE void update_axes(QtCharts::QAbstractAxis *axisX, QtCharts::QAbstractAxis *axisY){
if(axisX && axisY){
// Customize axis colors
QPen axisPen(QRgb(0xd18952));
axisPen.setWidth(2);
axisX->setLinePen(axisPen);
axisY->setLinePen(axisPen);
// Customize grid lines and shades
axisY->setShadesPen(Qt::NoPen);
axisY->setShadesBrush(QBrush(QColor(0x99, 0xcc, 0xcc, 0x55)));
}
}
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
QQmlApplicationEngine engine;
Helper helper;
engine.rootContext()->setContextProperty(QStringLiteral("helper"), &helper);
const QUrl url(QStringLiteral("qrc:/main.qml"));
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();
}
#include "main.moc"
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtCharts 2.0
ApplicationWindow {
id: root
visible: true
width: 640
height: 480
ChartView {
id: view
anchors.fill: parent
title: "Customchart example"
titleColor: "white"
titleFont.pixelSize: 18
antialiasing: true
LineSeries{
axisX: CategoryAxis {
id: xaxis
min: 0
max: 30
labelsFont.pixelSize: 12
gridVisible: false
labelsColor: "white"
CategoryRange {
label: "low"
endValue: 10
}
CategoryRange {
label: "optimal"
endValue: 20
}
CategoryRange {
label: "high"
endValue: 30
}
}
axisY: CategoryAxis {
id: yaxis
min: 0
max: 30
labelsFont.pixelSize: 12
gridVisible: false
labelsColor: "white"
shadesVisible: true
CategoryRange {
label: "slow"
endValue: 10
}
CategoryRange {
label: "med"
endValue: 20
}
CategoryRange {
label: "fast"
endValue: 30
}
}
color: "#fdb157"
width: 5
XYPoint { x: 0; y: 6 }
XYPoint { x: 9; y: 4 }
XYPoint { x: 15; y: 20 }
XYPoint { x: 25; y: 12 }
XYPoint { x: 29; y: 26 }
}
Component.onCompleted: {
view.legend.visible = false
helper.update_chart(view)
helper.update_axes(xaxis, yaxis)
}
}
}

Related

Regarding Qml bindings

Why the below code is not working when clicked on cancel button ie., parameterValueText.text is not setting to value 0.
I am assuming both val and parameterValueText.text are binded eachother. If I am wrong, please correct me
Please find below steps
Step 1: Edit a value in Text Input, for ex: 1
Step 2: on Keyboard accepted, parameterValueText.onAccepted is called
Step 3: Click on Save, saveBtn.onClicked is called
Step 4: Click on Cancel, cancelBtn.onClicked is called but the parameterValueText.text is not changed to value 0.
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtQuick.VirtualKeyboard 2.1
ApplicationWindow {
id: window
width: 480
height: 272
visible: true
title: qsTr("Keyboard")
property int val: 0
Column {
Item {
id: itemId
height: 20
width: window.width
Rectangle{
width: 100
height: itemId.height
border.color:"black"
TextInput {
id: parameterValueText
text: val //Assuming text and val are binded ??
inputMethodHints: Qt.ImhDigitsOnly
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
onAccepted: console.log("Value = ", parameterValueText.text)
}
}
}
Row {
spacing: 10
Button {
id: saveBtn
text: "Save"
onClicked: console.log("Save = ", parameterValueText.text)
}
Button {
id: cancelBtn
text: "Cancel"
onClicked: val = 0 //Why the value is not changing in parameterValueText.text ?
}
}
}
InputPanel
{
id:inputPanel
y:parent.height
width: window.width
//Background for Virtual Keyboard
Component{
id:keyboardBackground
Rectangle{
color:"#f4f6f3"//ScreenConfiguration.backGroundCanvas
}
}
states: State {
name: "visible"
when: inputPanel.active
PropertyChanges {
target: inputPanel
y: parent.height - inputPanel.height
}
PropertyChanges {
target: inputPanel
}
}
transitions: Transition {
from: ""
to: "visible"
reversible: true
ParallelAnimation {
NumberAnimation {
properties: "y"
duration: 200
easing.type: Easing.InOutQuad
}
}
}
Component.onCompleted: {
keyboard.style.keyboardBackground = keyboardBackground; // the keyboard background
}
}
}
main.cpp
int main(int argc, char *argv[])
{
qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
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();
}
Add below change in .pro file for enabling virtual keyboard
QT += quick virtualkeyboard
The console log ouputs are below
qml: Value = 1, val = 0
qml: Save = 1, val = 0
The binding isn't maintained after the user changes the text using the TextInput (as commenters suggested).
To achieve similar results to what you were attempting you could set val in the TextInput's onAccepted slot, and similarly update both val and parameterValueText.text in the onClicked slots for your buttons as appropriate. For example:
TextInput {
...
onAccepted: {
val = text
}
}
//...
Button {
id: cancelBtn
text: "Cancel"
onClicked: {
val = 0;
parameterValueText.text = val;
}
}
This won't maintain the initial bindings because the values are assigned in imperative javascript, but should achieve resetting values as expected.
As continuation on the answer of #dabbler, you can also recreate the binding in the onAccepted handler, but yeah it's still weird.
parameterValueText.text = Qt.binding(function() { return val; })
As another solution, you could have the val property bind to the text of the input (which then starts with "0"):
readonly property int val: parseInt(parameterValueText.text)
But that only works if the val property is intended to be used as readonly (otherwise you will loose the binding again). In case you want the val property to be set from outside, you could do the following:
onValChanged: parameterValueText.text = val
Note that this is assignment, not binding!

Qt Quick2 Material Dark Theme looks buggy/unreadable

I applied Material Dark Theme on my application as recommended on this question:
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickStyle>
#include <QDebug>
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
qputenv("QT_QUICK_CONTROLS_STYLE", QByteArray("Material"));
qputenv("QT_QUICK_CONTROLS_MATERIAL_THEME", QByteArray("Dark"));
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
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();
}
And my main.qml:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Button {
id: button
x: 145
y: 70
width: 151
height: 90
text: qsTr("Button")
}
RoundButton {
id: roundButton
x: 401
y: 120
text: "+"
}
CheckBox {
id: checkBox
x: 157
y: 297
text: qsTr("Check Box")
}
Slider {
id: slider
x: 345
y: 232
value: 0.5
}
DelayButton {
id: delayButton
x: 377
y: 344
text: qsTr("Delay Button")
}
Switch {
id: switch1
x: 151
y: 409
text: qsTr("Switch")
}
}
While default material works as expected, if I change to material dark it looks like this:
Material dark looks buggy:
I am pretty sure it shouldn't look like this. Did I miss to install something or do I need to import something additionally?
Using qtcreator on Windows, qml objects are just random unchanged controls.
That should work with the Window from Qt Quick. Please create a bug report for that. Using ApplicationWindow does work though.

How to slide QListView to the end and start from beginning?

I'm working on a project that gets news with API. I can clearly get the news from api and load them to the listview.
I simplify the code for telling my problem clearly.
Here is a 2 questions...
1 - I need to slide this list from top to the bottom basic sliding animation with given time. (eg. y from 0 to en of list with 5secs). The important point is the item count of the list can be changeable.
2 - When the animation reachs to the end of the list, I need to see the first item after the last item. But it has to be like this; after the last item of list, the first item has to shown( like infinite list) while the sliding process going on.
Here are my codes;
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QDebug>
#include <QQmlContext>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QStringList news = { "news01",
"news02",
"news03",
"news04",
"news05",
"news06",
"news07",
"news08",
"news09",
"news10",
"news11",
"news12",
"news13",
"news14",
"news15",
"news16",
"news17",
"news18",
"news19",
};
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("listNews",news);
const QUrl url(QStringLiteral("qrc:/main.qml"));
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();
}
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.0
import QtGraphicalEffects 1.0
import QtQuick.Layouts 1.3
Window {
id:pencere
visible: true
width: 640
height: 480
title: qsTr("Hello World")
color: "black"
ListView{
id: newsListView
implicitWidth: parent.width
implicitHeight: parent.height
model:listNews
spacing: 5
delegate: Rectangle {
id: delegateBackground
color:"#505051"
radius: 10
width: parent.width
height: contentContainer.height + 20
Item {
id: contentContainer
width: parent.width - 20
height: column.height
anchors.centerIn: delegateBackground
RowLayout {
width: parent.width
Rectangle {
id: newsicon
width: 16
height: 16
color: "steelblue"
Layout.alignment: Qt.AlignTop
}
ColumnLayout {
id: column
Layout.fillWidth: true
spacing: 100
Text {
Layout.fillWidth: true
Layout.alignment: Qt.AlignBottom
id: messageText
text: modelData
wrapMode: TextEdit.WordWrap
verticalAlignment: index %2 == 0 ? Text.AlignBottom : Text.AlignTop
color: "white"
}
}
}
}
}
}
}
For the first question you could add something like the following to your ListView. This will trigger an animation if you press the arrow key up/down. It isn't perfect, but it explains how to use NumberAnimations. The key to move the ListView content is the property contentY. If you want to scroll all the way to the bottom of the news feed you could calculate the position by using contentHeight of the ListView and the Window height.
ListView {
id: newsListView
property bool scrollUp: false
property bool scrollDown: false
focus: true
Keys.onUpPressed: newsListView.scrollUp = true
Keys.onDownPressed: newsListView.scrollDown = true
NumberAnimation on contentY {
running: newsListView.scrollDown
from: 0
to: newsListView.contentHeight
duration: 1000
onFinished: newsListView.scrollDown = false
}
NumberAnimation on contentY {
running: newsListView.scrollUp
from: newsListView.contentHeight
to: 0
duration: 1000
onFinished: newsListView.scrollUp = false
}
...
}
For the first question as proposed by #iam_peter, you can try using NumberAnimation to animate scrolling. For second query, in my opinion you can try researching on PathView as it is much easier to get circular list behavior in PathView without cumbersome index calculations.
Also, please have a look at this topic ListView Scrolling.

Make new window from separate file appear or disappear with QQmlApplicationEngine

I'm using QQmlApplicationEngine то load my main window.
QQmlApplicationEngine engine;
engine.load("GUI.qml");
Where GUI.qml is my main GUI file. How I can create and destruct a new window from code? As far as I can see, if I write engine.load("SecondWindow.qml");, how I can close it? Or I should create and destruct such objects from QML itself?
Option 1: You can do it from QML, see this sample:
import QtQuick 2.12
import QtQuick.Controls 2.5
ApplicationWindow {
id: window
visible: true
width: 640
height: 480
title: "Window 1"
CheckBox {
id: cb
text: "Show Window #2"
}
Loader {
active: cb.checked
sourceComponent: Component {
ApplicationWindow { // Or "SecondWindow"
visible: true
width: 640
height: 480
title: "Window 2"
}
}
}
}
Option 2: Also, you can control it from C++ side, for example like this:
QML
import QtQuick 2.12
import QtQuick.Controls 2.5
ApplicationWindow {
id: window
visible: true
width: 640
height: 480
title: "Window 1"
Loader {
active: showWindowFlag
// Instead of "sourceComponent" you can use
// source: "SecondWindow.qml"
sourceComponent: Component {
ApplicationWindow {
visible: true
width: 640
height: 480
title: "Window 2"
}
}
}
}
C++
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QTimer>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
bool showWindowFlag = false;
engine.rootContext()->setContextProperty("showWindowFlag", showWindowFlag); // !!!!
auto timer = new QTimer(&engine); // Parent will delete timer
QObject::connect(timer, &QTimer::timeout, [&](){
showWindowFlag = !showWindowFlag;
engine.rootContext()->setContextProperty("showWindowFlag", showWindowFlag);
});
timer->setInterval(1000);
timer->setSingleShot(false);
timer->start();
const QUrl url(QStringLiteral("qrc:/main.qml"));
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();
}

nextItemInFocusChain function not working in qml on mac?

I have a form where i've done a custom field that only accepts the top, right and bottom arrows.
But to accept the tab navigation i'm using the following chain of functions:
nextItemInFocusChain().forceActiveFocus()
The problem is that this is working on windows but not on mac...
I've a formulary to illustrate the problem where next to that "code text field" i have a comboBox where i want, that when the user clicks on tab in the "code text field" to navigate to.
It seems that it only navigates to other textFields, and a spinBox, like i have on the example seems to have a textField as a contentItem.
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
import QtQuick 2.9
import QtQuick.Controls 2.0
ApplicationWindow {
id: window
title: "Stack"
visible: true
height: 200
width: 400
Item {
id: page
anchors.fill: parent
width:parent.width
height: parent.height
Column{
width:parent.width
spacing:10
TextField {
id:textField
KeyNavigation.tab: spinBox1
implicitHeight: 30
font.bold: true
}
SpinBox {
id: spinBox1
width: 100
height: 30
editable: true
Component.onCompleted: contentItem.KeyNavigation.tab = userCodeField
}
PanelCodeField {
id: userCodeField
width: 100
height: 30
KeyNavigation.tab: comboBox
}
ComboBox {
id:comboBox
anchors.topMargin: 10
model: [ "Banana", "Apple", "Coconut" ]
KeyNavigation.tab: spinBox2
}
SpinBox {
id: spinBox2
width: 100
height: 30
editable: true
Component.onCompleted: contentItem.KeyNavigation.tab = textField
}
}
}
}
PanelCodeField.qml
import QtQuick 2.0
PanelTextField {
height: 479.9
visible: true
maximumLength: 5
font.pointSize: 12
property bool jumpOnTab: false
Keys.onPressed: {
var c
switch (event.key) {
case Qt.Key_Up:
c = String.fromCharCode(0x25B2)
break
case Qt.Key_Down:
c = String.fromCharCode(0x25BC)
break
case Qt.Key_Right:
c = String.fromCharCode(0x25B6)
break
case Qt.Key_Tab:
if(jumpOnTab)
nextItemInFocusChain().nextItemInFocusChain().forceActiveFocus()
else
nextItemInFocusChain().forceActiveFocus()
event.accepted = true
break
default:
event.accepted = true
break
}
if (!event.accepted) {
var s = text.concat(c)
text = s.substr(Math.max(0,s.length-maximumLength), maximumLength)
event.accepted = true
}
}
}
PanelTextField.qml
import QtQuick 2.0
import QtQuick.Controls 2.0
TextField {
property var linkedData
implicitHeight: 30
font.bold: true
implicitWidth:parent.width
}
Am i doing something wrong for the mac os x, or is there a workaround?
Open System Preferences > Keyboard > Shortcuts and select All Controls. By default macOS only allows tab navigation between "Text boxes and lists only".

Resources