Merge PR #730: Blur chart tooltips
This commit is contained in:
commit
e2e61b6637
@ -272,5 +272,6 @@
|
|||||||
<file>ui/mainviews/energy/StatsBase.qml</file>
|
<file>ui/mainviews/energy/StatsBase.qml</file>
|
||||||
<file>ui/mainviews/energy/EnergySettingsPage.qml</file>
|
<file>ui/mainviews/energy/EnergySettingsPage.qml</file>
|
||||||
<file>ui/mainviews/energy/ConsumersPieChart.qml</file>
|
<file>ui/mainviews/energy/ConsumersPieChart.qml</file>
|
||||||
|
<file>ui/components/NymeaToolTip.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
@ -239,13 +239,17 @@ Page {
|
|||||||
id: contentContainer
|
id: contentContainer
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
clip: true
|
clip: true
|
||||||
// visible: false
|
|
||||||
|
|
||||||
property int headerSize: 48
|
property int headerSize: 48
|
||||||
|
|
||||||
readonly property int scrollOffset: swipeView.currentItem.item.contentY
|
readonly property int scrollOffset: swipeView.currentItem.item.contentY
|
||||||
readonly property int headerBlurSize: Math.min(headerSize, scrollOffset * 2)
|
readonly property int headerBlurSize: Math.min(headerSize, scrollOffset * 2)
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: contentContainer.headerBlurSize
|
||||||
|
color: Style.backgroundColor
|
||||||
|
}
|
||||||
|
|
||||||
SwipeView {
|
SwipeView {
|
||||||
id: swipeView
|
id: swipeView
|
||||||
|
|||||||
@ -13,6 +13,8 @@ Item {
|
|||||||
property color tileOverlayForegroundColor: foregroundColor
|
property color tileOverlayForegroundColor: foregroundColor
|
||||||
property color tileOverlayIconColor: iconColor
|
property color tileOverlayIconColor: iconColor
|
||||||
|
|
||||||
|
property color tooltipBackgroundColor: tileOverlayColor
|
||||||
|
|
||||||
property int cornerRadius: 6
|
property int cornerRadius: 6
|
||||||
property int smallCornerRadius: 4
|
property int smallCornerRadius: 4
|
||||||
|
|
||||||
|
|||||||
43
nymea-app/ui/components/NymeaToolTip.qml
Normal file
43
nymea-app/ui/components/NymeaToolTip.qml
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import QtQuick 2.3
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import Nymea 1.0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property alias backgroundItem: blurEffectSource.sourceItem
|
||||||
|
property alias backgroundRect: blurEffectSource.sourceRect
|
||||||
|
|
||||||
|
Behavior on x { NumberAnimation { duration: Style.animationDuration } }
|
||||||
|
Behavior on y { NumberAnimation { duration: Style.animationDuration } }
|
||||||
|
Behavior on width { NumberAnimation { duration: Style.animationDuration } }
|
||||||
|
Behavior on height { NumberAnimation { duration: Style.animationDuration } }
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: blurSource
|
||||||
|
anchors.fill: toolTip
|
||||||
|
color: Style.backgroundColor
|
||||||
|
visible: false
|
||||||
|
radius: Style.smallCornerRadius
|
||||||
|
|
||||||
|
ShaderEffectSource {
|
||||||
|
id: blurEffectSource
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FastBlur {
|
||||||
|
anchors.fill: toolTip
|
||||||
|
source: blurSource
|
||||||
|
radius: 32
|
||||||
|
visible: toolTip.visible
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: Style.tooltipBackgroundColor
|
||||||
|
opacity: .5
|
||||||
|
radius: Style.smallCornerRadius
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -361,81 +361,72 @@ StatsBase {
|
|||||||
|
|
||||||
property var thingBarSetMap: ({})
|
property var thingBarSetMap: ({})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: chartView.x + chartView.plotArea.x
|
||||||
|
anchors.topMargin: chartView.y + chartView.plotArea.y
|
||||||
|
anchors.rightMargin: chartView.width - chartView.plotArea.width - chartView.plotArea.x
|
||||||
|
anchors.bottomMargin: chartView.height - chartView.plotArea.height - chartView.plotArea.y
|
||||||
|
|
||||||
MouseArea {
|
hoverEnabled: true
|
||||||
id: mouseArea
|
|
||||||
anchors.fill: chartView
|
|
||||||
anchors.leftMargin: chartView.plotArea.x
|
|
||||||
anchors.topMargin: chartView.plotArea.y
|
|
||||||
anchors.rightMargin: chartView.width - chartView.plotArea.width - chartView.plotArea.x
|
|
||||||
anchors.bottomMargin: chartView.height - chartView.plotArea.height - chartView.plotArea.y
|
|
||||||
|
|
||||||
hoverEnabled: true
|
Timer {
|
||||||
|
interval: 300
|
||||||
|
running: mouseArea.pressed
|
||||||
|
onTriggered: mouseArea.preventStealing = true
|
||||||
|
}
|
||||||
|
onReleased: mouseArea.preventStealing = false
|
||||||
|
|
||||||
Timer {
|
NymeaToolTip {
|
||||||
interval: 300
|
id: toolTip
|
||||||
running: mouseArea.pressed
|
|
||||||
onTriggered: mouseArea.preventStealing = true
|
backgroundItem: chartView
|
||||||
|
backgroundRect: Qt.rect(chartView.plotArea.x + toolTip.x, chartView.plotArea.y + toolTip.y, toolTip.width, toolTip.height)
|
||||||
|
|
||||||
|
property int idx: Math.floor(mouseArea.mouseX * categoryAxis.count / mouseArea.width)
|
||||||
|
visible: mouseArea.containsMouse
|
||||||
|
|
||||||
|
x: Math.min(idx * mouseArea.width / categoryAxis.count, mouseArea.width - width)
|
||||||
|
property double setMaxValue: {
|
||||||
|
var max = 0;
|
||||||
|
for (var i = 0; i < consumers.count; i++) {
|
||||||
|
var consumer = consumers.get(i)
|
||||||
|
max = barSeries.thingBarSetMap.hasOwnProperty(consumer.id) ? Math.max(max, barSeries.thingBarSetMap[consumer.id].at(idx)) : 0
|
||||||
}
|
}
|
||||||
onReleased: mouseArea.preventStealing = false
|
return max
|
||||||
|
}
|
||||||
|
y: Math.min(Math.max(mouseArea.height - (setMaxValue * mouseArea.height / valueAxis.max) - height - Style.smallMargins, 0), mouseArea.height - height)
|
||||||
|
|
||||||
Item {
|
width: tooltipLayout.implicitWidth + Style.smallMargins * 2
|
||||||
id: toolTip
|
height: tooltipLayout.implicitHeight + Style.smallMargins * 2
|
||||||
property int idx: Math.floor(mouseArea.mouseX * categoryAxis.count / mouseArea.width)
|
|
||||||
visible: mouseArea.containsMouse
|
|
||||||
|
|
||||||
x: Math.min(idx * mouseArea.width / categoryAxis.count, mouseArea.width - width)
|
ColumnLayout {
|
||||||
property double setMaxValue: {
|
id: tooltipLayout
|
||||||
var max = 0;
|
anchors {
|
||||||
for (var i = 0; i < consumers.count; i++) {
|
left: parent.left
|
||||||
var consumer = consumers.get(i)
|
top: parent.top
|
||||||
max = barSeries.thingBarSetMap.hasOwnProperty(consumer.id) ? Math.max(max, barSeries.thingBarSetMap[consumer.id].at(idx)) : 0
|
margins: Style.smallMargins
|
||||||
}
|
}
|
||||||
return max
|
Label {
|
||||||
}
|
text: toolTip.idx >= 0 && categoryAxis.timestamps.length > toolTip.idx ? root.configs[selectionTabs.currentValue.config].toLongLabel(categoryAxis.timestamps[toolTip.idx]) : ""
|
||||||
y: Math.min(Math.max(mouseArea.height - (setMaxValue * mouseArea.height / valueAxis.max) - height - Style.smallMargins, 0), mouseArea.height - height)
|
font: Style.smallFont
|
||||||
|
}
|
||||||
|
|
||||||
width: tooltipLayout.implicitWidth + Style.smallMargins * 2
|
Repeater {
|
||||||
height: tooltipLayout.implicitHeight + Style.smallMargins * 2
|
model: consumers
|
||||||
|
delegate: RowLayout {
|
||||||
Behavior on x { NumberAnimation { duration: Style.animationDuration } }
|
Rectangle {
|
||||||
Behavior on y { NumberAnimation { duration: Style.animationDuration } }
|
width: Style.extraSmallFont.pixelSize
|
||||||
Behavior on width { NumberAnimation { duration: Style.animationDuration } }
|
height: width
|
||||||
Behavior on height { NumberAnimation { duration: Style.animationDuration } }
|
color: index >= 0 ? root.colors[index % root.colors.length] : "white"
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: Style.tileOverlayColor
|
|
||||||
opacity: .8
|
|
||||||
radius: Style.smallCornerRadius
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: tooltipLayout
|
|
||||||
anchors {
|
|
||||||
left: parent.left
|
|
||||||
top: parent.top
|
|
||||||
margins: Style.smallMargins
|
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
text: toolTip.idx >= 0 && categoryAxis.timestamps.length > toolTip.idx ? root.configs[selectionTabs.currentValue.config].toLongLabel(categoryAxis.timestamps[toolTip.idx]) : ""
|
text: barSeries.thingBarSetMap.hasOwnProperty(model.id) ? "%1: %2 kWh".arg(model.name).arg(barSeries.thingBarSetMap[model.id].at(toolTip.idx).toFixed(2)) : ""
|
||||||
font: Style.smallFont
|
font: Style.extraSmallFont
|
||||||
}
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: consumers
|
|
||||||
delegate: RowLayout {
|
|
||||||
Rectangle {
|
|
||||||
width: Style.extraSmallFont.pixelSize
|
|
||||||
height: width
|
|
||||||
color: index >= 0 ? root.colors[index % root.colors.length] : "white"
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
text: barSeries.thingBarSetMap.hasOwnProperty(model.id) ? "%1: %2 kWh".arg(model.name).arg(barSeries.thingBarSetMap[model.id].at(toolTip.idx).toFixed(2)) : ""
|
|
||||||
font: Style.extraSmallFont
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,24 +3,12 @@ import QtCharts 2.3
|
|||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import Nymea 1.0
|
import Nymea 1.0
|
||||||
|
import "qrc:/ui/components"
|
||||||
|
|
||||||
ChartView {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
backgroundColor: "transparent"
|
|
||||||
margins.left: 0
|
|
||||||
margins.right: 0
|
|
||||||
margins.bottom: 0
|
|
||||||
margins.top: 0
|
|
||||||
|
|
||||||
property var colors: null
|
property var colors: null
|
||||||
|
|
||||||
title: qsTr("Consumers history")
|
|
||||||
titleColor: Style.foregroundColor
|
|
||||||
|
|
||||||
legend.alignment: Qt.AlignBottom
|
|
||||||
legend.labelColor: Style.foregroundColor
|
|
||||||
legend.font: Style.extraSmallFont
|
|
||||||
|
|
||||||
property ThingsProxy consumers: null
|
property ThingsProxy consumers: null
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
@ -119,7 +107,7 @@ ChartView {
|
|||||||
thingPowerLogs.loadingInhibited = true;
|
thingPowerLogs.loadingInhibited = true;
|
||||||
|
|
||||||
for (var thingId in d.thingsSeriesMap) {
|
for (var thingId in d.thingsSeriesMap) {
|
||||||
root.removeSeries(d.thingsSeriesMap[thingId])
|
chartView.removeSeries(d.thingsSeriesMap[thingId])
|
||||||
}
|
}
|
||||||
d.thingsSeriesMap = ({})
|
d.thingsSeriesMap = ({})
|
||||||
|
|
||||||
@ -130,17 +118,17 @@ ChartView {
|
|||||||
var baseSeries = zeroSeries;
|
var baseSeries = zeroSeries;
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
baseSeries = d.thingsSeriesMap[consumerThingIds[i-1]].upperSeries
|
baseSeries = d.thingsSeriesMap[consumerThingIds[i-1]].upperSeries
|
||||||
// print("base for:", thing.name, "is", engine.thingManager.things.getThing(consumerThingIds[i-1]).name)
|
// print("base for:", thing.name, "is", engine.thingManager.things.getThing(consumerThingIds[i-1]).name)
|
||||||
}
|
}
|
||||||
|
|
||||||
var series = root.createSeries(ChartView.SeriesTypeArea, thing.name, dateTimeAxis, valueAxis)
|
var series = chartView.createSeries(ChartView.SeriesTypeArea, thing.name, dateTimeAxis, valueAxis)
|
||||||
series.lowerSeries = baseSeries
|
series.lowerSeries = baseSeries
|
||||||
series.upperSeries = lineSeriesComponent.createObject(series)
|
series.upperSeries = lineSeriesComponent.createObject(series)
|
||||||
series.color = root.colors[i % root.colors.length]
|
series.color = root.colors[i % root.colors.length]
|
||||||
series.borderWidth = 0;
|
series.borderWidth = 0;
|
||||||
series.borderColor = series.color
|
series.borderColor = series.color
|
||||||
|
|
||||||
// print("Adding thingId series", thing.id, thing.name)
|
// print("Adding thingId series", thing.id, thing.name)
|
||||||
var map = d.thingsSeriesMap
|
var map = d.thingsSeriesMap
|
||||||
map[thing.id] = series
|
map[thing.id] = series
|
||||||
d.thingsSeriesMap = map
|
d.thingsSeriesMap = map
|
||||||
@ -156,95 +144,116 @@ ChartView {
|
|||||||
LineSeries { }
|
LineSeries { }
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueAxis {
|
ChartView {
|
||||||
id: valueAxis
|
id: chartView
|
||||||
min: 0
|
anchors.fill: parent
|
||||||
max: Math.ceil(powerBalanceLogs.maxValue / 1000) * 1000
|
|
||||||
labelFormat: ""
|
|
||||||
gridLineColor: Style.tileOverlayColor
|
|
||||||
labelsVisible: false
|
|
||||||
lineVisible: false
|
|
||||||
titleVisible: false
|
|
||||||
shadesVisible: false
|
|
||||||
// visible: false
|
|
||||||
|
|
||||||
}
|
backgroundColor: "transparent"
|
||||||
|
margins.left: 0
|
||||||
|
margins.right: 0
|
||||||
|
margins.bottom: 0
|
||||||
|
margins.top: 0
|
||||||
|
|
||||||
Item {
|
|
||||||
id: labelsLayout
|
title: qsTr("Consumers history")
|
||||||
x: Style.smallMargins
|
titleColor: Style.foregroundColor
|
||||||
y: root.plotArea.y
|
|
||||||
height: root.plotArea.height
|
legend.alignment: Qt.AlignBottom
|
||||||
width: plotArea.x - x
|
legend.labelColor: Style.foregroundColor
|
||||||
Repeater {
|
legend.font: Style.extraSmallFont
|
||||||
model: valueAxis.tickCount
|
|
||||||
delegate: Label {
|
|
||||||
y: parent.height / (valueAxis.tickCount - 1) * index - font.pixelSize / 2
|
ValueAxis {
|
||||||
width: parent.width - Style.smallMargins
|
id: valueAxis
|
||||||
horizontalAlignment: Text.AlignRight
|
min: 0
|
||||||
text: ((valueAxis.max - (index * valueAxis.max / (valueAxis.tickCount - 1))) / 1000).toFixed(2) + "kW"
|
max: Math.ceil(powerBalanceLogs.maxValue / 1000) * 1000
|
||||||
verticalAlignment: Text.AlignTop
|
labelFormat: ""
|
||||||
font: Style.extraSmallFont
|
gridLineColor: Style.tileOverlayColor
|
||||||
|
labelsVisible: false
|
||||||
|
lineVisible: false
|
||||||
|
titleVisible: false
|
||||||
|
shadesVisible: false
|
||||||
|
// visible: false
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: labelsLayout
|
||||||
|
x: Style.smallMargins
|
||||||
|
y: chartView.plotArea.y
|
||||||
|
height: chartView.plotArea.height
|
||||||
|
width: chartView.plotArea.x - x
|
||||||
|
Repeater {
|
||||||
|
model: valueAxis.tickCount
|
||||||
|
delegate: Label {
|
||||||
|
y: parent.height / (valueAxis.tickCount - 1) * index - font.pixelSize / 2
|
||||||
|
width: parent.width - Style.smallMargins
|
||||||
|
horizontalAlignment: Text.AlignRight
|
||||||
|
text: ((valueAxis.max - (index * valueAxis.max / (valueAxis.tickCount - 1))) / 1000).toFixed(2) + "kW"
|
||||||
|
verticalAlignment: Text.AlignTop
|
||||||
|
font: Style.extraSmallFont
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
DateTimeAxis {
|
DateTimeAxis {
|
||||||
id: dateTimeAxis
|
id: dateTimeAxis
|
||||||
property date now: new Date()
|
property date now: new Date()
|
||||||
min: {
|
min: {
|
||||||
var date = new Date(now);
|
var date = new Date(now);
|
||||||
date.setTime(date.getTime() - (1000 * 60 * 60 * 24) + 2000);
|
date.setTime(date.getTime() - (1000 * 60 * 60 * 24) + 2000);
|
||||||
return date;
|
return date;
|
||||||
|
}
|
||||||
|
max: {
|
||||||
|
var date = new Date(now);
|
||||||
|
date.setTime(date.getTime() + 2000)
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
format: "hh:mm"
|
||||||
|
labelsFont: Style.extraSmallFont
|
||||||
|
gridVisible: false
|
||||||
|
minorGridVisible: false
|
||||||
|
lineVisible: false
|
||||||
|
shadesVisible: false
|
||||||
|
labelsColor: Style.foregroundColor
|
||||||
}
|
}
|
||||||
max: {
|
|
||||||
var date = new Date(now);
|
|
||||||
date.setTime(date.getTime() + 2000)
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
format: "hh:mm"
|
|
||||||
labelsFont: Style.extraSmallFont
|
|
||||||
gridVisible: false
|
|
||||||
minorGridVisible: false
|
|
||||||
lineVisible: false
|
|
||||||
shadesVisible: false
|
|
||||||
labelsColor: Style.foregroundColor
|
|
||||||
}
|
|
||||||
|
|
||||||
AreaSeries {
|
AreaSeries {
|
||||||
id: consumptionSeries
|
id: consumptionSeries
|
||||||
axisX: dateTimeAxis
|
axisX: dateTimeAxis
|
||||||
axisY: valueAxis
|
axisY: valueAxis
|
||||||
color: Style.gray
|
color: Style.gray
|
||||||
borderWidth: 0
|
borderWidth: 0
|
||||||
borderColor: color
|
borderColor: color
|
||||||
name: qsTr("Unknown")
|
name: qsTr("Unknown")
|
||||||
|
|
||||||
lowerSeries: LineSeries {
|
lowerSeries: LineSeries {
|
||||||
id: zeroSeries
|
id: zeroSeries
|
||||||
XYPoint { x: dateTimeAxis.min.getTime(); y: 0 }
|
XYPoint { x: dateTimeAxis.min.getTime(); y: 0 }
|
||||||
XYPoint { x: dateTimeAxis.max.getTime(); y: 0 }
|
XYPoint { x: dateTimeAxis.max.getTime(); y: 0 }
|
||||||
function update(timestamp) {
|
function update(timestamp) {
|
||||||
append(timestamp, 0);
|
append(timestamp, 0);
|
||||||
removePoints(1,1);
|
removePoints(1,1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
upperSeries: LineSeries {
|
||||||
|
id: consumptionUpperSeries
|
||||||
|
}
|
||||||
|
|
||||||
|
function addEntry(entry) {
|
||||||
|
consumptionUpperSeries.append(entry.timestamp.getTime(), entry.consumption)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
upperSeries: LineSeries {
|
|
||||||
id: consumptionUpperSeries
|
|
||||||
}
|
|
||||||
|
|
||||||
function addEntry(entry) {
|
|
||||||
consumptionUpperSeries.append(entry.timestamp.getTime(), entry.consumption)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.leftMargin: root.plotArea.x
|
anchors.leftMargin: chartView.plotArea.x
|
||||||
anchors.topMargin: root.plotArea.y
|
anchors.topMargin: chartView.plotArea.y
|
||||||
anchors.rightMargin: root.width - root.plotArea.width - root.plotArea.x
|
anchors.rightMargin: chartView.width - chartView.plotArea.width - chartView.plotArea.x
|
||||||
anchors.bottomMargin: root.height - root.plotArea.height - root.plotArea.y
|
anchors.bottomMargin: chartView.height - chartView.plotArea.height - chartView.plotArea.y
|
||||||
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
|
||||||
@ -263,10 +272,13 @@ ChartView {
|
|||||||
visible: mouseArea.containsMouse
|
visible: mouseArea.containsMouse
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
NymeaToolTip {
|
||||||
id: toolTip
|
id: toolTip
|
||||||
visible: mouseArea.containsMouse
|
visible: mouseArea.containsMouse
|
||||||
|
|
||||||
|
backgroundItem: chartView
|
||||||
|
backgroundRect: Qt.rect(mouseArea.x + toolTip.x, mouseArea.y + toolTip.y, toolTip.width, toolTip.height)
|
||||||
|
|
||||||
property int idx: consumptionUpperSeries.count - Math.floor(mouseArea.mouseX * consumptionUpperSeries.count / mouseArea.width)
|
property int idx: consumptionUpperSeries.count - Math.floor(mouseArea.mouseX * consumptionUpperSeries.count / mouseArea.width)
|
||||||
property int seriesIndex: consumptionUpperSeries.count - idx
|
property int seriesIndex: consumptionUpperSeries.count - idx
|
||||||
|
|
||||||
@ -281,18 +293,6 @@ ChartView {
|
|||||||
|
|
||||||
property date timestamp: new Date(consumptionUpperSeries.at(seriesIndex).x)
|
property date timestamp: new Date(consumptionUpperSeries.at(seriesIndex).x)
|
||||||
|
|
||||||
Behavior on x { NumberAnimation { duration: Style.animationDuration } }
|
|
||||||
Behavior on y { NumberAnimation { duration: Style.animationDuration } }
|
|
||||||
Behavior on width { NumberAnimation { duration: Style.animationDuration } }
|
|
||||||
Behavior on height { NumberAnimation { duration: Style.animationDuration } }
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: Style.tileOverlayColor
|
|
||||||
opacity: .8
|
|
||||||
radius: Style.smallCornerRadius
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: tooltipLayout
|
id: tooltipLayout
|
||||||
anchors {
|
anchors {
|
||||||
|
|||||||
@ -316,107 +316,98 @@ StatsBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
anchors.fill: chartView
|
anchors.fill: parent
|
||||||
anchors.leftMargin: chartView.plotArea.x
|
anchors.leftMargin: chartView.x + chartView.plotArea.x
|
||||||
anchors.topMargin: chartView.plotArea.y
|
anchors.topMargin: chartView.y + chartView.plotArea.y
|
||||||
anchors.rightMargin: chartView.width - chartView.plotArea.width - chartView.plotArea.x
|
anchors.rightMargin: chartView.width - chartView.plotArea.width - chartView.plotArea.x
|
||||||
anchors.bottomMargin: chartView.height - chartView.plotArea.height - chartView.plotArea.y
|
anchors.bottomMargin: chartView.height - chartView.plotArea.height - chartView.plotArea.y
|
||||||
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
interval: 300
|
interval: 300
|
||||||
running: mouseArea.pressed
|
running: mouseArea.pressed
|
||||||
onTriggered: mouseArea.preventStealing = true
|
onTriggered: mouseArea.preventStealing = true
|
||||||
|
}
|
||||||
|
onReleased: mouseArea.preventStealing = false
|
||||||
|
|
||||||
|
NymeaToolTip {
|
||||||
|
id: toolTip
|
||||||
|
|
||||||
|
backgroundItem: chartView
|
||||||
|
backgroundRect: Qt.rect(chartView.plotArea.x + toolTip.x, chartView.plotArea.y + toolTip.y, toolTip.width, toolTip.height)
|
||||||
|
|
||||||
|
property int idx: Math.floor(mouseArea.mouseX * categoryAxis.count / mouseArea.width)
|
||||||
|
visible: mouseArea.containsMouse
|
||||||
|
|
||||||
|
x: Math.min(idx * mouseArea.width / categoryAxis.count, mouseArea.width - width)
|
||||||
|
property double setMaxValue: d.consumptionSet && d.productionSet && d.acquisitionSet && d.returnSet ?
|
||||||
|
Math.max(d.consumptionSet.at(idx), Math.max(d.productionSet.at(idx), Math.max(d.acquisitionSet.at(idx), d.returnSet.at(idx))))
|
||||||
|
: 0
|
||||||
|
y: Math.min(Math.max(mouseArea.height - (setMaxValue * mouseArea.height / valueAxis.max) - height - Style.smallMargins, 0), mouseArea.height - height)
|
||||||
|
width: tooltipLayout.implicitWidth + Style.smallMargins * 2
|
||||||
|
height: tooltipLayout.implicitHeight + Style.smallMargins * 2
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: tooltipLayout
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
top: parent.top
|
||||||
|
margins: Style.smallMargins
|
||||||
}
|
}
|
||||||
onReleased: mouseArea.preventStealing = false
|
|
||||||
|
|
||||||
Item {
|
Label {
|
||||||
id: toolTip
|
text: toolTip.idx >= 0 && categoryAxis.timestamps.length > toolTip.idx ? root.configs[selectionTabs.currentValue.config].toLongLabel(categoryAxis.timestamps[toolTip.idx]) : ""
|
||||||
property int idx: Math.floor(mouseArea.mouseX * categoryAxis.count / mouseArea.width)
|
font: Style.smallFont
|
||||||
visible: mouseArea.containsMouse
|
}
|
||||||
|
|
||||||
x: Math.min(idx * mouseArea.width / categoryAxis.count, mouseArea.width - width)
|
|
||||||
property double setMaxValue: d.consumptionSet && d.productionSet && d.acquisitionSet && d.returnSet ?
|
|
||||||
Math.max(d.consumptionSet.at(idx), Math.max(d.productionSet.at(idx), Math.max(d.acquisitionSet.at(idx), d.returnSet.at(idx))))
|
|
||||||
: 0
|
|
||||||
y: Math.min(Math.max(mouseArea.height - (setMaxValue * mouseArea.height / valueAxis.max) - height - Style.smallMargins, 0), mouseArea.height - height)
|
|
||||||
width: tooltipLayout.implicitWidth + Style.smallMargins * 2
|
|
||||||
height: tooltipLayout.implicitHeight + Style.smallMargins * 2
|
|
||||||
|
|
||||||
Behavior on x { NumberAnimation { duration: Style.animationDuration } }
|
|
||||||
Behavior on y { NumberAnimation { duration: Style.animationDuration } }
|
|
||||||
Behavior on width { NumberAnimation { duration: Style.animationDuration } }
|
|
||||||
Behavior on height { NumberAnimation { duration: Style.animationDuration } }
|
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
width: Style.extraSmallFont.pixelSize
|
||||||
color: Style.tileOverlayColor
|
height: width
|
||||||
opacity: .8
|
color: Style.blue
|
||||||
radius: Style.smallCornerRadius
|
|
||||||
}
|
}
|
||||||
|
Label {
|
||||||
|
text: d.consumptionSet ? qsTr("Consumed: %1 kWh").arg(d.consumptionSet.at(toolTip.idx).toFixed(2)) : ""
|
||||||
ColumnLayout {
|
font: Style.extraSmallFont
|
||||||
id: tooltipLayout
|
}
|
||||||
anchors {
|
}
|
||||||
left: parent.left
|
RowLayout {
|
||||||
top: parent.top
|
Rectangle {
|
||||||
margins: Style.smallMargins
|
width: Style.extraSmallFont.pixelSize
|
||||||
}
|
height: width
|
||||||
|
color: Style.green
|
||||||
Label {
|
}
|
||||||
text: toolTip.idx >= 0 && categoryAxis.timestamps.length > toolTip.idx ? root.configs[selectionTabs.currentValue.config].toLongLabel(categoryAxis.timestamps[toolTip.idx]) : ""
|
Label {
|
||||||
font: Style.smallFont
|
text: d.productionSet ? qsTr("Produced: %1 kWh").arg(d.productionSet.at(toolTip.idx).toFixed(2)) : ""
|
||||||
}
|
font: Style.extraSmallFont
|
||||||
|
}
|
||||||
RowLayout {
|
}
|
||||||
Rectangle {
|
RowLayout {
|
||||||
width: Style.extraSmallFont.pixelSize
|
Rectangle {
|
||||||
height: width
|
width: Style.extraSmallFont.pixelSize
|
||||||
color: Style.blue
|
height: width
|
||||||
}
|
color: Style.red
|
||||||
Label {
|
}
|
||||||
text: d.consumptionSet ? qsTr("Consumed: %1 kWh").arg(d.consumptionSet.at(toolTip.idx).toFixed(2)) : ""
|
Label {
|
||||||
font: Style.extraSmallFont
|
text: d.acquisitionSet ? qsTr("From grid: %1 kWh").arg(d.acquisitionSet.at(toolTip.idx).toFixed(2)) : ""
|
||||||
}
|
font: Style.extraSmallFont
|
||||||
}
|
}
|
||||||
RowLayout {
|
}
|
||||||
Rectangle {
|
RowLayout {
|
||||||
width: Style.extraSmallFont.pixelSize
|
Rectangle {
|
||||||
height: width
|
width: Style.extraSmallFont.pixelSize
|
||||||
color: Style.green
|
height: width
|
||||||
}
|
color: Style.orange
|
||||||
Label {
|
}
|
||||||
text: d.productionSet ? qsTr("Produced: %1 kWh").arg(d.productionSet.at(toolTip.idx).toFixed(2)) : ""
|
Label {
|
||||||
font: Style.extraSmallFont
|
text: d.returnSet ? qsTr("To grid: %1 kWh").arg(d.returnSet.at(toolTip.idx).toFixed(2)) : ""
|
||||||
}
|
font: Style.extraSmallFont
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
Rectangle {
|
|
||||||
width: Style.extraSmallFont.pixelSize
|
|
||||||
height: width
|
|
||||||
color: Style.red
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
text: d.acquisitionSet ? qsTr("From grid: %1 kWh").arg(d.acquisitionSet.at(toolTip.idx).toFixed(2)) : ""
|
|
||||||
font: Style.extraSmallFont
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
Rectangle {
|
|
||||||
width: Style.extraSmallFont.pixelSize
|
|
||||||
height: width
|
|
||||||
color: Style.orange
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
text: d.returnSet ? qsTr("To grid: %1 kWh").arg(d.returnSet.at(toolTip.idx).toFixed(2)) : ""
|
|
||||||
font: Style.extraSmallFont
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,22 +2,12 @@ import QtQuick 2.0
|
|||||||
import QtCharts 2.3
|
import QtCharts 2.3
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
import Nymea 1.0
|
import Nymea 1.0
|
||||||
|
import "qrc:/ui/components"
|
||||||
|
|
||||||
ChartView {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
backgroundColor: "transparent"
|
|
||||||
margins.left: 0
|
|
||||||
margins.right: 0
|
|
||||||
margins.bottom: 0
|
|
||||||
margins.top: 0
|
|
||||||
|
|
||||||
title: qsTr("My consumption history")
|
|
||||||
titleColor: Style.foregroundColor
|
|
||||||
|
|
||||||
legend.alignment: Qt.AlignBottom
|
|
||||||
legend.labelColor: Style.foregroundColor
|
|
||||||
legend.font: Style.extraSmallFont
|
|
||||||
|
|
||||||
property PowerBalanceLogs energyLogs: PowerBalanceLogs {
|
property PowerBalanceLogs energyLogs: PowerBalanceLogs {
|
||||||
id: powerBalanceLogs
|
id: powerBalanceLogs
|
||||||
@ -63,179 +53,198 @@ ChartView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueAxis {
|
ChartView {
|
||||||
id: valueAxis
|
id: chartView
|
||||||
min: 0
|
anchors.fill: parent
|
||||||
max: Math.ceil(powerBalanceLogs.maxValue / 1000) * 1000
|
backgroundColor: "transparent"
|
||||||
labelFormat: ""
|
margins.left: 0
|
||||||
gridLineColor: Style.tileOverlayColor
|
margins.right: 0
|
||||||
labelsVisible: false
|
margins.bottom: 0
|
||||||
lineVisible: false
|
margins.top: 0
|
||||||
titleVisible: false
|
|
||||||
shadesVisible: false
|
|
||||||
// visible: false
|
|
||||||
|
|
||||||
}
|
title: qsTr("My consumption history")
|
||||||
|
titleColor: Style.foregroundColor
|
||||||
|
|
||||||
Item {
|
legend.alignment: Qt.AlignBottom
|
||||||
id: labelsLayout
|
legend.labelColor: Style.foregroundColor
|
||||||
x: Style.smallMargins
|
legend.font: Style.extraSmallFont
|
||||||
y: root.plotArea.y
|
|
||||||
height: root.plotArea.height
|
|
||||||
width: plotArea.x - x
|
ValueAxis {
|
||||||
Repeater {
|
id: valueAxis
|
||||||
model: valueAxis.tickCount
|
min: 0
|
||||||
delegate: Label {
|
max: Math.ceil(powerBalanceLogs.maxValue / 1000) * 1000
|
||||||
y: parent.height / (valueAxis.tickCount - 1) * index - font.pixelSize / 2
|
labelFormat: ""
|
||||||
width: parent.width - Style.smallMargins
|
gridLineColor: Style.tileOverlayColor
|
||||||
horizontalAlignment: Text.AlignRight
|
labelsVisible: false
|
||||||
text: ((valueAxis.max - (index * valueAxis.max / (valueAxis.tickCount - 1))) / 1000).toFixed(2) + "kW"
|
lineVisible: false
|
||||||
verticalAlignment: Text.AlignTop
|
titleVisible: false
|
||||||
font: Style.extraSmallFont
|
shadesVisible: false
|
||||||
|
// visible: false
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: labelsLayout
|
||||||
|
x: Style.smallMargins
|
||||||
|
y: chartView.plotArea.y
|
||||||
|
height: chartView.plotArea.height
|
||||||
|
width: chartView.plotArea.x - x
|
||||||
|
Repeater {
|
||||||
|
model: valueAxis.tickCount
|
||||||
|
delegate: Label {
|
||||||
|
y: parent.height / (valueAxis.tickCount - 1) * index - font.pixelSize / 2
|
||||||
|
width: parent.width - Style.smallMargins
|
||||||
|
horizontalAlignment: Text.AlignRight
|
||||||
|
text: ((valueAxis.max - (index * valueAxis.max / (valueAxis.tickCount - 1))) / 1000).toFixed(2) + "kW"
|
||||||
|
verticalAlignment: Text.AlignTop
|
||||||
|
font: Style.extraSmallFont
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTimeAxis {
|
||||||
|
id: dateTimeAxis
|
||||||
|
property date now: new Date()
|
||||||
|
min: {
|
||||||
|
var date = new Date(now);
|
||||||
|
date.setTime(date.getTime() - (1000 * 60 * 60 * 24) + 2000);
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
max: {
|
||||||
|
var date = new Date(now);
|
||||||
|
date.setTime(date.getTime() + 2000)
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
format: "hh:mm"
|
||||||
|
labelsFont: Style.extraSmallFont
|
||||||
|
gridVisible: false
|
||||||
|
minorGridVisible: false
|
||||||
|
lineVisible: false
|
||||||
|
shadesVisible: false
|
||||||
|
labelsColor: Style.foregroundColor
|
||||||
|
}
|
||||||
|
|
||||||
|
// For debugging, to see the total graph and check if the other maths line up
|
||||||
|
AreaSeries {
|
||||||
|
id: consumptionSeries
|
||||||
|
axisX: dateTimeAxis
|
||||||
|
axisY: valueAxis
|
||||||
|
color: "blue"
|
||||||
|
borderWidth: 0
|
||||||
|
borderColor: color
|
||||||
|
opacity: .5
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
lowerSeries: zeroSeries
|
||||||
|
upperSeries: LineSeries {
|
||||||
|
id: consumptionUpperSeries
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateValue(entry) {
|
||||||
|
return entry.consumption
|
||||||
|
}
|
||||||
|
function addEntry(entry) {
|
||||||
|
consumptionUpperSeries.append(entry.timestamp.getTime(), calculateValue(entry))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
DateTimeAxis {
|
AreaSeries {
|
||||||
id: dateTimeAxis
|
id: selfProductionSeries
|
||||||
property date now: new Date()
|
axisX: dateTimeAxis
|
||||||
min: {
|
axisY: valueAxis
|
||||||
var date = new Date(now);
|
color: Style.green
|
||||||
date.setTime(date.getTime() - (1000 * 60 * 60 * 24) + 2000);
|
borderWidth: 0
|
||||||
return date;
|
borderColor: color
|
||||||
}
|
name: qsTr("Self production")
|
||||||
max: {
|
// visible: false
|
||||||
var date = new Date(now);
|
|
||||||
date.setTime(date.getTime() + 2000)
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
format: "hh:mm"
|
|
||||||
labelsFont: Style.extraSmallFont
|
|
||||||
gridVisible: false
|
|
||||||
minorGridVisible: false
|
|
||||||
lineVisible: false
|
|
||||||
shadesVisible: false
|
|
||||||
labelsColor: Style.foregroundColor
|
|
||||||
}
|
|
||||||
|
|
||||||
// For debugging, to see the total graph and check if the other maths line up
|
lowerSeries: LineSeries {
|
||||||
AreaSeries {
|
id: zeroSeries
|
||||||
id: consumptionSeries
|
XYPoint { x: dateTimeAxis.min.getTime(); y: 0 }
|
||||||
axisX: dateTimeAxis
|
XYPoint { x: dateTimeAxis.max.getTime(); y: 0 }
|
||||||
axisY: valueAxis
|
function update(timestamp) {
|
||||||
color: "blue"
|
append(timestamp, 0);
|
||||||
borderWidth: 0
|
removePoints(1,1);
|
||||||
borderColor: color
|
}
|
||||||
opacity: .5
|
}
|
||||||
visible: false
|
|
||||||
|
|
||||||
lowerSeries: zeroSeries
|
upperSeries: LineSeries {
|
||||||
upperSeries: LineSeries {
|
id: selfProductionUpperSeries
|
||||||
id: consumptionUpperSeries
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function calculateValue(entry) {
|
function calculateValue(entry) {
|
||||||
return entry.consumption
|
var value = entry.consumption - Math.max(0, entry.acquisition);
|
||||||
}
|
if (entry.storage < 0) {
|
||||||
function addEntry(entry) {
|
value += entry.storage;
|
||||||
consumptionUpperSeries.append(entry.timestamp.getTime(), calculateValue(entry))
|
}
|
||||||
}
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addEntry(entry) {
|
||||||
AreaSeries {
|
selfProductionUpperSeries.append(entry.timestamp.getTime(), calculateValue(entry))
|
||||||
id: selfProductionSeries
|
|
||||||
axisX: dateTimeAxis
|
|
||||||
axisY: valueAxis
|
|
||||||
color: Style.green
|
|
||||||
borderWidth: 0
|
|
||||||
borderColor: color
|
|
||||||
name: qsTr("Self production")
|
|
||||||
// visible: false
|
|
||||||
|
|
||||||
lowerSeries: LineSeries {
|
|
||||||
id: zeroSeries
|
|
||||||
XYPoint { x: dateTimeAxis.min.getTime(); y: 0 }
|
|
||||||
XYPoint { x: dateTimeAxis.max.getTime(); y: 0 }
|
|
||||||
function update(timestamp) {
|
|
||||||
append(timestamp, 0);
|
|
||||||
removePoints(1,1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
upperSeries: LineSeries {
|
AreaSeries {
|
||||||
id: selfProductionUpperSeries
|
id: storageSeries
|
||||||
}
|
axisX: dateTimeAxis
|
||||||
|
axisY: valueAxis
|
||||||
|
color: Style.orange
|
||||||
|
borderWidth: 0
|
||||||
|
borderColor: color
|
||||||
|
name: qsTr("From battery")
|
||||||
|
// visible: false
|
||||||
|
|
||||||
function calculateValue(entry) {
|
lowerSeries: selfProductionUpperSeries
|
||||||
var value = entry.consumption - Math.max(0, entry.acquisition);
|
upperSeries: LineSeries {
|
||||||
if (entry.storage < 0) {
|
id: storageUpperSeries
|
||||||
value += entry.storage;
|
}
|
||||||
|
|
||||||
|
function calculateValue(entry) {
|
||||||
|
return selfProductionSeries.calculateValue(entry) + Math.abs(Math.min(0, entry.storage));
|
||||||
|
}
|
||||||
|
|
||||||
|
function addEntry(entry) {
|
||||||
|
storageUpperSeries.append(entry.timestamp.getTime(), calculateValue(entry))
|
||||||
}
|
}
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function addEntry(entry) {
|
|
||||||
selfProductionUpperSeries.append(entry.timestamp.getTime(), calculateValue(entry))
|
AreaSeries {
|
||||||
|
id: acquisitionSeries
|
||||||
|
axisX: dateTimeAxis
|
||||||
|
axisY: valueAxis
|
||||||
|
color: Style.red
|
||||||
|
borderWidth: 0
|
||||||
|
borderColor: color
|
||||||
|
name: qsTr("From grid")
|
||||||
|
// visible: false
|
||||||
|
|
||||||
|
lowerSeries: storageUpperSeries
|
||||||
|
upperSeries: LineSeries {
|
||||||
|
id: acquisitionUpperSeries
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateValue(entry) {
|
||||||
|
return storageSeries.calculateValue(entry) + Math.max(0, entry.acquisition)
|
||||||
|
}
|
||||||
|
function addEntry(entry) {
|
||||||
|
acquisitionUpperSeries.append(entry.timestamp.getTime(), calculateValue(entry))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AreaSeries {
|
|
||||||
id: storageSeries
|
|
||||||
axisX: dateTimeAxis
|
|
||||||
axisY: valueAxis
|
|
||||||
color: Style.orange
|
|
||||||
borderWidth: 0
|
|
||||||
borderColor: color
|
|
||||||
name: qsTr("From battery")
|
|
||||||
// visible: false
|
|
||||||
|
|
||||||
lowerSeries: selfProductionUpperSeries
|
|
||||||
upperSeries: LineSeries {
|
|
||||||
id: storageUpperSeries
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateValue(entry) {
|
|
||||||
return selfProductionSeries.calculateValue(entry) + Math.abs(Math.min(0, entry.storage));
|
|
||||||
}
|
|
||||||
|
|
||||||
function addEntry(entry) {
|
|
||||||
storageUpperSeries.append(entry.timestamp.getTime(), calculateValue(entry))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
AreaSeries {
|
|
||||||
id: acquisitionSeries
|
|
||||||
axisX: dateTimeAxis
|
|
||||||
axisY: valueAxis
|
|
||||||
color: Style.red
|
|
||||||
borderWidth: 0
|
|
||||||
borderColor: color
|
|
||||||
name: qsTr("From grid")
|
|
||||||
// visible: false
|
|
||||||
|
|
||||||
lowerSeries: storageUpperSeries
|
|
||||||
upperSeries: LineSeries {
|
|
||||||
id: acquisitionUpperSeries
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateValue(entry) {
|
|
||||||
return storageSeries.calculateValue(entry) + Math.max(0, entry.acquisition)
|
|
||||||
}
|
|
||||||
function addEntry(entry) {
|
|
||||||
acquisitionUpperSeries.append(entry.timestamp.getTime(), calculateValue(entry))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
anchors.fill: parent
|
anchors.fill: chartView
|
||||||
anchors.leftMargin: root.plotArea.x
|
anchors.leftMargin: chartView.plotArea.x
|
||||||
anchors.topMargin: root.plotArea.y
|
anchors.topMargin: chartView.plotArea.y
|
||||||
anchors.rightMargin: root.width - root.plotArea.width - root.plotArea.x
|
anchors.rightMargin: chartView.width - chartView.plotArea.width - chartView.plotArea.x
|
||||||
anchors.bottomMargin: root.height - root.plotArea.height - root.plotArea.y
|
anchors.bottomMargin: chartView.height - chartView.plotArea.height - chartView.plotArea.y
|
||||||
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
|
||||||
@ -254,10 +263,15 @@ ChartView {
|
|||||||
visible: mouseArea.containsMouse
|
visible: mouseArea.containsMouse
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
|
||||||
|
NymeaToolTip {
|
||||||
id: toolTip
|
id: toolTip
|
||||||
visible: mouseArea.containsMouse
|
visible: mouseArea.containsMouse
|
||||||
|
|
||||||
|
backgroundItem: chartView
|
||||||
|
backgroundRect: Qt.rect(mouseArea.x + toolTip.x, mouseArea.y + toolTip.y, toolTip.width, toolTip.height)
|
||||||
|
|
||||||
|
|
||||||
property int idx: consumptionUpperSeries.count - (Math.floor(mouseArea.mouseX * consumptionUpperSeries.count / mouseArea.width))
|
property int idx: consumptionUpperSeries.count - (Math.floor(mouseArea.mouseX * consumptionUpperSeries.count / mouseArea.width))
|
||||||
property int seriesIndex: consumptionUpperSeries.count - idx
|
property int seriesIndex: consumptionUpperSeries.count - idx
|
||||||
|
|
||||||
@ -270,18 +284,6 @@ ChartView {
|
|||||||
width: tooltipLayout.implicitWidth + Style.smallMargins * 2
|
width: tooltipLayout.implicitWidth + Style.smallMargins * 2
|
||||||
height: tooltipLayout.implicitHeight + Style.smallMargins * 2
|
height: tooltipLayout.implicitHeight + Style.smallMargins * 2
|
||||||
|
|
||||||
Behavior on x { NumberAnimation { duration: Style.animationDuration } }
|
|
||||||
Behavior on y { NumberAnimation { duration: Style.animationDuration } }
|
|
||||||
Behavior on width { NumberAnimation { duration: Style.animationDuration } }
|
|
||||||
Behavior on height { NumberAnimation { duration: Style.animationDuration } }
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: Style.tileOverlayColor
|
|
||||||
opacity: .8
|
|
||||||
radius: Style.smallCornerRadius
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: tooltipLayout
|
id: tooltipLayout
|
||||||
anchors {
|
anchors {
|
||||||
@ -334,3 +336,4 @@ ChartView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,21 +3,10 @@ import QtCharts 2.3
|
|||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import Nymea 1.0
|
import Nymea 1.0
|
||||||
|
import "qrc:/ui/components"
|
||||||
|
|
||||||
ChartView {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
backgroundColor: "transparent"
|
|
||||||
margins.left: 0
|
|
||||||
margins.right: 0
|
|
||||||
margins.bottom: 0
|
|
||||||
margins.top: 0
|
|
||||||
|
|
||||||
title: qsTr("My production history")
|
|
||||||
titleColor: Style.foregroundColor
|
|
||||||
|
|
||||||
legend.alignment: Qt.AlignBottom
|
|
||||||
legend.labelColor: Style.foregroundColor
|
|
||||||
legend.font: Style.extraSmallFont
|
|
||||||
|
|
||||||
property PowerBalanceLogs energyLogs: PowerBalanceLogs {
|
property PowerBalanceLogs energyLogs: PowerBalanceLogs {
|
||||||
id: powerBalanceLogs
|
id: powerBalanceLogs
|
||||||
@ -62,173 +51,192 @@ ChartView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueAxis {
|
ChartView {
|
||||||
id: valueAxis
|
id: chartView
|
||||||
min: 0
|
anchors.fill: parent
|
||||||
max: Math.ceil(-powerBalanceLogs.minValue / 1000) * 1000
|
|
||||||
labelFormat: ""
|
|
||||||
gridLineColor: Style.tileOverlayColor
|
|
||||||
labelsVisible: false
|
|
||||||
lineVisible: false
|
|
||||||
titleVisible: false
|
|
||||||
shadesVisible: false
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
backgroundColor: "transparent"
|
||||||
id: labelsLayout
|
margins.left: 0
|
||||||
x: Style.smallMargins
|
margins.right: 0
|
||||||
y: root.plotArea.y
|
margins.bottom: 0
|
||||||
height: root.plotArea.height
|
margins.top: 0
|
||||||
width: plotArea.x - x
|
|
||||||
Repeater {
|
|
||||||
model: valueAxis.tickCount
|
|
||||||
delegate: Label {
|
|
||||||
y: parent.height / (valueAxis.tickCount - 1) * index - font.pixelSize / 2
|
|
||||||
width: parent.width - Style.smallMargins
|
|
||||||
horizontalAlignment: Text.AlignRight
|
|
||||||
text: ((valueAxis.max - (index * valueAxis.max / (valueAxis.tickCount - 1))) / 1000).toFixed(2) + "kW"
|
|
||||||
verticalAlignment: Text.AlignTop
|
|
||||||
font: Style.extraSmallFont
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DateTimeAxis {
|
title: qsTr("My production history")
|
||||||
id: dateTimeAxis
|
titleColor: Style.foregroundColor
|
||||||
property date now: new Date()
|
|
||||||
min: {
|
|
||||||
var date = new Date(now);
|
|
||||||
date.setTime(date.getTime() - (1000 * 60 * 60 * 24) + 2000);
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
max: {
|
|
||||||
var date = new Date(now);
|
|
||||||
date.setTime(date.getTime() + 2000)
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
format: "hh:mm"
|
|
||||||
labelsFont: Style.extraSmallFont
|
|
||||||
gridVisible: false
|
|
||||||
minorGridVisible: false
|
|
||||||
lineVisible: false
|
|
||||||
shadesVisible: false
|
|
||||||
labelsColor: Style.foregroundColor
|
|
||||||
}
|
|
||||||
|
|
||||||
// For debugging, to see if the other maths line up with the plain production graph
|
legend.alignment: Qt.AlignBottom
|
||||||
AreaSeries {
|
legend.labelColor: Style.foregroundColor
|
||||||
id: productionSeries
|
legend.font: Style.extraSmallFont
|
||||||
axisX: dateTimeAxis
|
|
||||||
axisY: valueAxis
|
|
||||||
color: "blue"
|
|
||||||
borderWidth: 0
|
|
||||||
borderColor: color
|
|
||||||
opacity: .5
|
|
||||||
name: "Total production"
|
|
||||||
visible: false
|
|
||||||
|
|
||||||
function calculateValue(entry) {
|
|
||||||
return Math.abs(Math.min(0, entry.production))
|
ValueAxis {
|
||||||
}
|
id: valueAxis
|
||||||
function addEntry(entry) {
|
min: 0
|
||||||
productionUpperSeries.append(entry.timestamp.getTime(), calculateValue(entry))
|
max: Math.ceil(-powerBalanceLogs.minValue / 1000) * 1000
|
||||||
|
labelFormat: ""
|
||||||
|
gridLineColor: Style.tileOverlayColor
|
||||||
|
labelsVisible: false
|
||||||
|
lineVisible: false
|
||||||
|
titleVisible: false
|
||||||
|
shadesVisible: false
|
||||||
}
|
}
|
||||||
|
|
||||||
lowerSeries: zeroSeries
|
Item {
|
||||||
upperSeries: LineSeries {
|
id: labelsLayout
|
||||||
id: productionUpperSeries
|
x: Style.smallMargins
|
||||||
}
|
y: chartView.plotArea.y
|
||||||
}
|
height: chartView.plotArea.height
|
||||||
|
width: chartView.plotArea.x - x
|
||||||
AreaSeries {
|
Repeater {
|
||||||
id: selfConsumptionSeries
|
model: valueAxis.tickCount
|
||||||
axisX: dateTimeAxis
|
delegate: Label {
|
||||||
axisY: valueAxis
|
y: parent.height / (valueAxis.tickCount - 1) * index - font.pixelSize / 2
|
||||||
color: Style.red
|
width: parent.width - Style.smallMargins
|
||||||
borderWidth: 0
|
horizontalAlignment: Text.AlignRight
|
||||||
borderColor: color
|
text: ((valueAxis.max - (index * valueAxis.max / (valueAxis.tickCount - 1))) / 1000).toFixed(2) + "kW"
|
||||||
name: qsTr("Consumed")
|
verticalAlignment: Text.AlignTop
|
||||||
// visible: false
|
font: Style.extraSmallFont
|
||||||
|
}
|
||||||
function calculateValue(entry) {
|
|
||||||
return Math.abs(Math.min(0, entry.production)) - Math.abs(Math.min(0, entry.acquisition)) - Math.max(0, entry.storage)
|
|
||||||
}
|
|
||||||
|
|
||||||
function addEntry(entry) {
|
|
||||||
selfConsumptionUpperSeries.append(entry.timestamp.getTime(), calculateValue(entry))
|
|
||||||
}
|
|
||||||
|
|
||||||
lowerSeries: LineSeries {
|
|
||||||
id: zeroSeries
|
|
||||||
XYPoint { x: dateTimeAxis.min.getTime(); y: 0 }
|
|
||||||
XYPoint { x: dateTimeAxis.max.getTime(); y: 0 }
|
|
||||||
function update(timestamp) {
|
|
||||||
append(timestamp, 0);
|
|
||||||
removePoints(1,1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
upperSeries: LineSeries {
|
DateTimeAxis {
|
||||||
id: selfConsumptionUpperSeries
|
id: dateTimeAxis
|
||||||
}
|
property date now: new Date()
|
||||||
}
|
min: {
|
||||||
|
var date = new Date(now);
|
||||||
AreaSeries {
|
date.setTime(date.getTime() - (1000 * 60 * 60 * 24) + 2000);
|
||||||
id: storageSeries
|
return date;
|
||||||
axisX: dateTimeAxis
|
}
|
||||||
axisY: valueAxis
|
max: {
|
||||||
color: Style.orange
|
var date = new Date(now);
|
||||||
borderWidth: 0
|
date.setTime(date.getTime() + 2000)
|
||||||
borderColor: color
|
return date;
|
||||||
// visible: false
|
}
|
||||||
name: qsTr("To battery")
|
format: "hh:mm"
|
||||||
|
labelsFont: Style.extraSmallFont
|
||||||
|
gridVisible: false
|
||||||
function calculateValue(entry) {
|
minorGridVisible: false
|
||||||
return selfConsumptionSeries.calculateValue(entry) + Math.abs(Math.max(0, entry.storage));
|
lineVisible: false
|
||||||
|
shadesVisible: false
|
||||||
|
labelsColor: Style.foregroundColor
|
||||||
}
|
}
|
||||||
|
|
||||||
function addEntry(entry) {
|
// For debugging, to see if the other maths line up with the plain production graph
|
||||||
storageUpperSeries.append(entry.timestamp.getTime(), calculateValue(entry))
|
AreaSeries {
|
||||||
|
id: productionSeries
|
||||||
|
axisX: dateTimeAxis
|
||||||
|
axisY: valueAxis
|
||||||
|
color: "blue"
|
||||||
|
borderWidth: 0
|
||||||
|
borderColor: color
|
||||||
|
opacity: .5
|
||||||
|
name: "Total production"
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
function calculateValue(entry) {
|
||||||
|
return Math.abs(Math.min(0, entry.production))
|
||||||
|
}
|
||||||
|
function addEntry(entry) {
|
||||||
|
productionUpperSeries.append(entry.timestamp.getTime(), calculateValue(entry))
|
||||||
|
}
|
||||||
|
|
||||||
|
lowerSeries: zeroSeries
|
||||||
|
upperSeries: LineSeries {
|
||||||
|
id: productionUpperSeries
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lowerSeries: selfConsumptionUpperSeries
|
AreaSeries {
|
||||||
upperSeries: LineSeries {
|
id: selfConsumptionSeries
|
||||||
id: storageUpperSeries
|
axisX: dateTimeAxis
|
||||||
}
|
axisY: valueAxis
|
||||||
}
|
color: Style.red
|
||||||
|
borderWidth: 0
|
||||||
|
borderColor: color
|
||||||
|
name: qsTr("Consumed")
|
||||||
|
// visible: false
|
||||||
|
|
||||||
|
function calculateValue(entry) {
|
||||||
|
return Math.abs(Math.min(0, entry.production)) - Math.abs(Math.min(0, entry.acquisition)) - Math.max(0, entry.storage)
|
||||||
|
}
|
||||||
|
|
||||||
AreaSeries {
|
function addEntry(entry) {
|
||||||
id: acquisitionSeries
|
selfConsumptionUpperSeries.append(entry.timestamp.getTime(), calculateValue(entry))
|
||||||
axisX: dateTimeAxis
|
}
|
||||||
axisY: valueAxis
|
|
||||||
color: Style.green
|
|
||||||
borderWidth: 0
|
|
||||||
borderColor: color
|
|
||||||
name: qsTr("To grid")
|
|
||||||
// visible: false
|
|
||||||
|
|
||||||
function calculateValue(entry) {
|
lowerSeries: LineSeries {
|
||||||
return storageSeries.calculateValue(entry) + Math.abs(Math.min(0, entry.acquisition))
|
id: zeroSeries
|
||||||
}
|
XYPoint { x: dateTimeAxis.min.getTime(); y: 0 }
|
||||||
function addEntry(entry) {
|
XYPoint { x: dateTimeAxis.max.getTime(); y: 0 }
|
||||||
acquisitionUpperSeries.append(entry.timestamp.getTime(), calculateValue(entry))
|
function update(timestamp) {
|
||||||
|
append(timestamp, 0);
|
||||||
|
removePoints(1,1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
upperSeries: LineSeries {
|
||||||
|
id: selfConsumptionUpperSeries
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lowerSeries: storageUpperSeries
|
AreaSeries {
|
||||||
upperSeries: LineSeries {
|
id: storageSeries
|
||||||
id: acquisitionUpperSeries
|
axisX: dateTimeAxis
|
||||||
|
axisY: valueAxis
|
||||||
|
color: Style.orange
|
||||||
|
borderWidth: 0
|
||||||
|
borderColor: color
|
||||||
|
// visible: false
|
||||||
|
name: qsTr("To battery")
|
||||||
|
|
||||||
|
|
||||||
|
function calculateValue(entry) {
|
||||||
|
return selfConsumptionSeries.calculateValue(entry) + Math.abs(Math.max(0, entry.storage));
|
||||||
|
}
|
||||||
|
|
||||||
|
function addEntry(entry) {
|
||||||
|
storageUpperSeries.append(entry.timestamp.getTime(), calculateValue(entry))
|
||||||
|
}
|
||||||
|
|
||||||
|
lowerSeries: selfConsumptionUpperSeries
|
||||||
|
upperSeries: LineSeries {
|
||||||
|
id: storageUpperSeries
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AreaSeries {
|
||||||
|
id: acquisitionSeries
|
||||||
|
axisX: dateTimeAxis
|
||||||
|
axisY: valueAxis
|
||||||
|
color: Style.green
|
||||||
|
borderWidth: 0
|
||||||
|
borderColor: color
|
||||||
|
name: qsTr("To grid")
|
||||||
|
// visible: false
|
||||||
|
|
||||||
|
function calculateValue(entry) {
|
||||||
|
return storageSeries.calculateValue(entry) + Math.abs(Math.min(0, entry.acquisition))
|
||||||
|
}
|
||||||
|
function addEntry(entry) {
|
||||||
|
acquisitionUpperSeries.append(entry.timestamp.getTime(), calculateValue(entry))
|
||||||
|
}
|
||||||
|
|
||||||
|
lowerSeries: storageUpperSeries
|
||||||
|
upperSeries: LineSeries {
|
||||||
|
id: acquisitionUpperSeries
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.leftMargin: root.plotArea.x
|
anchors.leftMargin: chartView.plotArea.x
|
||||||
anchors.topMargin: root.plotArea.y
|
anchors.topMargin: chartView.plotArea.y
|
||||||
anchors.rightMargin: root.width - root.plotArea.width - root.plotArea.x
|
anchors.rightMargin: chartView.width - chartView.plotArea.width - chartView.plotArea.x
|
||||||
anchors.bottomMargin: root.height - root.plotArea.height - root.plotArea.y
|
anchors.bottomMargin: chartView.height - chartView.plotArea.height - chartView.plotArea.y
|
||||||
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
|
||||||
@ -247,10 +255,13 @@ ChartView {
|
|||||||
visible: mouseArea.containsMouse
|
visible: mouseArea.containsMouse
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
NymeaToolTip {
|
||||||
id: toolTip
|
id: toolTip
|
||||||
visible: mouseArea.containsMouse
|
visible: mouseArea.containsMouse
|
||||||
|
|
||||||
|
backgroundItem: chartView
|
||||||
|
backgroundRect: Qt.rect(mouseArea.x + toolTip.x, mouseArea.y + toolTip.y, toolTip.width, toolTip.height)
|
||||||
|
|
||||||
property int idx: productionUpperSeries.count - Math.floor(mouseArea.mouseX * productionUpperSeries.count / mouseArea.width)
|
property int idx: productionUpperSeries.count - Math.floor(mouseArea.mouseX * productionUpperSeries.count / mouseArea.width)
|
||||||
property int seriesIndex: productionUpperSeries.count - idx
|
property int seriesIndex: productionUpperSeries.count - idx
|
||||||
|
|
||||||
@ -263,18 +274,6 @@ ChartView {
|
|||||||
width: tooltipLayout.implicitWidth + Style.smallMargins * 2
|
width: tooltipLayout.implicitWidth + Style.smallMargins * 2
|
||||||
height: tooltipLayout.implicitHeight + Style.smallMargins * 2
|
height: tooltipLayout.implicitHeight + Style.smallMargins * 2
|
||||||
|
|
||||||
Behavior on x { NumberAnimation { duration: Style.animationDuration } }
|
|
||||||
Behavior on y { NumberAnimation { duration: Style.animationDuration } }
|
|
||||||
Behavior on width { NumberAnimation { duration: Style.animationDuration } }
|
|
||||||
Behavior on height { NumberAnimation { duration: Style.animationDuration } }
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: Style.tileOverlayColor
|
|
||||||
opacity: .8
|
|
||||||
radius: Style.smallCornerRadius
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: tooltipLayout
|
id: tooltipLayout
|
||||||
anchors {
|
anchors {
|
||||||
@ -327,3 +326,6 @@ ChartView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user