Merge PR #730: Blur chart tooltips

This commit is contained in:
Jenkins nymea 2021-12-15 00:49:47 +01:00
commit e2e61b6637
9 changed files with 645 additions and 608 deletions

View File

@ -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>

View File

@ -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

View File

@ -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

View 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
}
}

View File

@ -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
}
}
} }
} }
} }

View File

@ -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 {

View File

@ -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
}
}
} }
} }
} }

View File

@ -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 {
} }
} }
} }

View File

@ -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 {
} }
} }
} }