QT Candlestick Chart with real-time data - qtcharts

There is a cool Candlestick Chart in QT6.
and there is an example on how to add data into the chart with QML:
import QtQuick 2.0
import QtCharts 2.2
ChartView {
title: "Candlestick series"
width: 800
height: 600
theme: ChartView.ChartThemeLight
legend.alignment: Qt.AlignBottom
antialiasing: true
CandlestickSeries {
name: "Acme Ltd."
increasingColor: "green"
decreasingColor: "red"
CandlestickSet { timestamp: 1435708800000; open: 6.90; high: 6.94; low: 5.99; close: 6.60 }
CandlestickSet { timestamp: 1435795200000; open: 6.69; high: 6.69; low: 6.69; close: 6.69 }
CandlestickSet { timestamp: 1436140800000; open: 4.85; high: 6.23; low: 4.85; close: 6.00 }
CandlestickSet { timestamp: 1436227200000; open: 5.89; high: 6.15; low: 3.77; close: 5.69 }
CandlestickSet { timestamp: 1436313600000; open: 4.64; high: 4.64; low: 2.54; close: 2.54 }
}
}
but what if the data is real-time and the last candle can vary and new candles can be added? Is it possible to modify the candles (chart data) without a flickering?
For example, CandlestickSeries has add/remove but not changed signals.

Related

Make custom fixed intervals chartjs

Need to make fixed interval with number values[0,100000,300000,500000,5000000] for left side of graph as this:
scales: {
y: {
max: 5000000 ,
beginAtZero: true,
grid: {
color: '#FFFFFF05',
},
ticks:{
color: '#FFFFFF20',
font: {
size: 10,
},
count: 5,
}
},
Actual intervals, that depends on count.
One way I found depends on values: https://www.chartjs.org/docs/latest/samples/scales/log.html
I don't need any dependencies, just fixed numbers.

Vue Chart.js - simple dot/line on bar chart

I'd need to add a simple dot/vertical line on my bar chart that has a dynamical X value, and 0 for Y value. Preview of what I need (the red dot):
Where the green values are dynamic.
Preview of my current state:
Where 3.30 should be the X coordinate of the dot - [3.30, 0].
I'm using Vue chart for the charts and I tried do create a mixed one with the bar and scatter but scatter requires type: 'linear' for it's xAxis which doesn't suit my need for the bar chart.
So I tried with chartjs-plugin-annotation and it's box type which accepts "coordinates" but the problem here is that the X value must be a fixed value on the X axis (labels object). If I put for the X axis [3,0] it will work, but if there is a decimal number, like [3.5, 0], it won't work.
// data
options: {
responsive: true,
maintainAspectRatio: false,
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
min: 0,
max: 1,
stepSize: 0.1
}
}]
}
}
// computed
labels: [1, 2, 3, 4, 5, 6], // fixed value, there are always 6 bars
datasets: [
{
label: 'Values',
backgroundColor: '#f89098',
data: this.tableInputValues // array of decimal values
}
]
So, my question is how to put a "simple" dot, or a vertical line, on a Chart.js bar chart where the dot has a dynamical value for the X axis -> [dynamic value, 0].
FYI - it's about Expected value
As far as I understand Vue Chart works using canvas (as seen on Demo page).
So, my suggestion here is to retrieve the canvas node representing the chart in your DOM and dynamically write the desired dot. For example:
var c = document.getElementById("bar-chart"); //hereby assuming canvas named "bar-chart"
var ctx = c.getContext("2d");
ctx.fillStyle = "#ff0000"; //red color for the dot
ctx.beginPath();
let yPosition = c.height - 5; //fixed y position
let xPosition = 35; //that's the dynamic expected value
ctx.arc(xPosition, yPosition, 2.5, 0, 2 * Math.PI);
ctx.fill();
Here you find a demo showing how to achieve that using Vue. In this scenario, you need to wrap the code to draw a dot on the canvas in a afterDraw hook. This hook needs to be attached to the chart component as a plugin, so like this:
...
mounted () {
//adding the plugin to draw the red dot
this.addPlugin({
id: 'chart-plugin',
afterDraw: function (chart) {
var c = chart.canvas;
var ctx = c.getContext("2d");
ctx.fillStyle = "#ff0000";
ctx.beginPath();
let xPosition = 742; //x positioning to be calculated according to your needs
let yPosition = c.height - 28;
ctx.arc(xPosition, yPosition, 3, 0, 2 * Math.PI);
ctx.fill();
}
});
//actual chart rendering
this.renderChart({
...
});
}
...
For the sake of completeness, here you find the list of all the available hooks of Chart.js plugin API.
This is my solution for your problem https://jsfiddle.net/huynhsamha/e54djwxp/
And this is screenshot for the result
In my solution, I use type="line" and both x-axis and y-axis with type="linear". I also add property options to <chart> for using options in ChartJS
<div id="vue">
<chart type="line" :data="data" :options="options"></chart>
</div>
The options will set-up x-axis and y-axis to render the data points and expected value:
options: {
scales: {
xAxes: [{
type: 'linear',
ticks: {
min: 1,
max: 6,
stepSize: 1
}
}],
yAxes: [{
type: 'linear',
ticks: {
min: 0,
max: 1,
stepSize: 0.1
}
}]
}
}
And the data will have 2 datasets. The first is data points, use type line, and the second is the expected value which use type bubble.
data: {
datasets: [{
label: 'Frequency Data',
data: dataPoints.map(({ val, freq }) => ({
x: val,
y: freq
})),
backgroundColor: 'rgba(72, 202, 59, 0.4)',
borderColor: 'rgba(72, 202, 59, 1)'
}, {
label: 'Expected Value',
type: 'bubble',
data: [{
x: expectedValue,
y: 0,
r: 8 // radius
}],
backgroundColor: 'rgba(255, 68, 0, 0.4)',
borderColor: 'rgba(255, 68, 0, 1)'
}]
},
In datasets, we have dataPoints and expectedValue, it will be fetched from API to get your data points. I also simulate the simple API for data points:
// simulate the API to get data points
const retrieveData = () => [
{ val: 1, freq: 0.15 },
{ val: 2, freq: 0.25 },
{ val: 3, freq: 0.3 },
{ val: 4, freq: 0.2 },
{ val: 5, freq: 0.1 },
{ val: 6, freq: 0.45 }
]
// fetch your data here, return array of JSON { val, freg }
const dataPoints = retrieveData() || [];
// calculate expected value = sum( val * freq ) each i in dataPoints
const expectedValue = dataPoints.reduce((cur, { val, freq }) => cur + val * freq, 0).toFixed(4);
You can run snippet or run on fiddle https://jsfiddle.net/huynhsamha/e54djwxp/92/
<script async src="//jsfiddle.net/huynhsamha/e54djwxp/92/embed/js,html,css,result/dark/"></script>

3D callout line doesn't work with client-side graphics

I am struggling to use callout line with client-side graphics.
I followed the "Point styles for cities" example which uses a feature layer from the "LyonPointsOfInterest (FeatureServer)".
But it doesn't work with a feature layer which creates client-side graphics based on data returned from a web service.
Is there a limitation on 3d callout line?
Here's my code snippet:
Create a feature layer based on layer definition:
featureLayer = new FeatureLayer({
fields: this.layerDefinition.fields,
objectIdField: this.layerDefinition.objectIdField,
geometryType: this.layerDefinition.geometryType,
id: this.layerId
});
Set elevation and feature reduction and renderer:
featureLayer.elevationInfo = {
// elevation mode that will place points on top of the buildings or other SceneLayer 3D objects
mode: "relative-to-scene"
};
// feature reduction is set to selection because our scene contains too many points and they overlap
featureLayer.featureReduction = {
type: "selection"
};
featureLayer.renderer = this._getUniqueValueRenderer() as any as Renderer// callout render
Here the renderer code:
_getUniqueValueRenderer() {
let verticalOffset = { // verticalOffset shifts the symbol vertically
screenLength: 150, // callout line length
maxWorldLength: 200,
minWorldLength: 35
},
uniqueValueRenderer = {
type: "unique-value", // autocasts as new UniqueValueRenderer()
field: "AQHI",
uniqueValueInfos: [{
value: 1,
symbol: this._get3DCallOutSymbol(verticalOffset, "Museum.png", "#D13470")
}, {
value: 2,
symbol: this._get3DCallOutSymbol(verticalOffset, "Restaurant.png", "#F97C5A")
}, {
value: 3,
symbol: this._get3DCallOutSymbol(verticalOffset, "Church.png", "#884614")
}, {
value: 4,
symbol: this._get3DCallOutSymbol(verticalOffset, "Hotel.png", "#56B2D6")
}, {
value: 5,
symbol: this._get3DCallOutSymbol(verticalOffset, "Park.png", "#40C2B4")
}, {
value: 6,
symbol: this._get3DCallOutSymbol(verticalOffset, "Museum.png", "#D13470")
}, {
value: 7,
symbol: this._get3DCallOutSymbol(verticalOffset, "beer.png", "#F97C5A")
}, {
value: 8,
symbol: this._get3DCallOutSymbol(verticalOffset, "senate.png", "#884614")
}, {
value: 9,
symbol: this._get3DCallOutSymbol(verticalOffset, "Hotel.png", "#56B2D6")
}, {
value: 10,
symbol: this._get3DCallOutSymbol(verticalOffset, "Park.png", "#40C2B4")
}
]};
return uniqueValueRenderer;
}
_get3DCallOutSymbol(verticalOffset: any, iconName: string, color: string) {
return {
type: "point-3d", // autocasts as new PointSymbol3D()
symbolLayers: [{
type: "icon", // autocasts as new IconSymbol3DLayer()
resource: {
href: this.iconPath + iconName
},
size: 20,
outline: {
color: "white",
size: 2
}
}],
verticalOffset: verticalOffset,
callout: {
type: "line", // autocasts as new LineCallout3D()
color: "white",
size: 2,
border: {
color: color
}
}
};
}
Set source to an array of graphics, generated based web service data
featureLayer.source = graphics;
You should not use this.layerDefinition in the instanciation a your new FeatureLayer, but put it in a new var. Idem for this.layerId :
var layerDef = this.layerDefinition;
var lyrId = this.layerId;
featureLayer = new FeatureLayer({
fields: layerDef.fields,
objectIdField: layerDef.objectIdField,
geometryType: layerDef.geometryType,
id: lyrId
});
because this. at this place, is in the scope of new Feature()

Property binding doesn't work as expected

I'm trying just now to implement a list with complicated data. In my case it should be an array like this:
[
{ item: "itemName1", objects: [x1,x1,x1] },
{ item: "itemName2", objects: [x2,x2,x2] }
]
As I know there are 2 ways to do that. First of them is to expose QObjectList-based model from C++ to QML. Than currently not interested for me since I want to implement that in pure QML. But ListElement's roles must contain only simple constants, line strings, numbers etc. i.e. no arrays.
So I did the data source as external array:
import QtQuick 2.5
import QtQuick.Layouts 1.2
import QtQuick.Window 2.0
Window {
width: 500
height: 500
id: window
ListView {
id: listView
anchors.fill: parent
anchors.margins: 2
spacing: 2
property var data: [ { name: "item1", objects: [1,2,3]} ]
model: data.length
delegate: Rectangle {
width: parent.width
height: content.height
property int itemIndex: index
color: index % 2 ? "#EFEFEF" : "#DEDEDE"
RowLayout {
id: content
spacing: 10
Text {
text: listView.data[itemIndex].name
}
Column {
Repeater {
model: listView.data[itemIndex].objects.length
Text {
text: listView.data[itemIndex].objects[index]
}
}
}
}
}
Component.onCompleted: {
listView.data.push( { name: "item2", objects: [4,5,6] } );
listView.data.push( { name: "item3", objects: [7,8,9] } );
listView.model = listView.data.length; // it doesnt't work without this line
}
}
}
It works fine with static array. The problem appears when I fill the array dynamically. In this case the property binding (model: data.length) suddenly stops working. So the workaround is to set it manually but I don't like this solution.

How to call functions without onClick in Qt .qml file?

How to call functions without onClick in Qt .qml file?
Item {
id: btnSk
width: state == "wait" : lSkip.width + 3
height: parent.height
visible: player.iW
states: [
State {
name: "skip"
when: player.iWt < 0
PropertyChanges {
target: skip_layout
visible: false
}
}
]
MouseArea {
id: wait_layout
anchors.fill: parent
visible: false
onClicked: player.skipSmt();
}
}
Need to call player.skipSmt() without onClicked. How can I do it?
If you want the function player.skipSmt() to be called on startup of your Item named btnSk, then you could do the following:
Item {
id: btnSk
width: state == "wait" : lSkip.width + 3
height: parent.height
visible: player.iW
...
states: [
// your states
]
MouseArea {
// your mouse area
}
Component.onCompleted: player.skipSmt()
}
P.S. this assumes that player is an id accessible in your Item named btnSk
P.P.S. If this is not what you want, then you should pay heed to the comment given by MrEricSir!