Implemented a screen displaying the history of operations with the board. Added dialogs for displaying messages and statuses

This commit is contained in:
Yury Shuvakin
2022-08-26 07:34:04 +03:00
parent 79518df613
commit 6ae4386b37
18 changed files with 535 additions and 61 deletions

View File

@@ -46,7 +46,8 @@ ComboBox {
} }
onPaint: { onPaint: {
context.reset(); var context = getContext("2d")
context.reset()
if (control.down) { if (control.down) {
context.moveTo(0, height); context.moveTo(0, height);
@@ -77,7 +78,10 @@ ComboBox {
background: Rectangle { background: Rectangle {
implicitWidth: 120 implicitWidth: 120
implicitHeight: 46 implicitHeight: 46
color: control.down ? Palette.selectedBackgroundColor : Palette.backgroundColor color: control.enabled
? (control.down ? Palette.selectedBackgroundColor : Palette.backgroundColor)
: Palette.hoveredBackgroundColor
border.color: Palette.borderColor border.color: Palette.borderColor
border.width: 1 border.width: 1
radius: 5 radius: 5

View File

@@ -0,0 +1,8 @@
import QtQuick 2.12
import Utils 1.0
Rectangle {
color: Palette.screenBackgroundColor
radius: 6
}

View File

@@ -0,0 +1,75 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import Controls 1.0 as Controls
import Utils 1.0
Rectangle {
color: Qt.darker(Palette.hoveredBackgroundColor, 1.05)
height: 50
radius: 6
property Dialog dialog: undefined
RowLayout {
anchors.fill: parent
Controls.SubtitleLabel {
text: root.title
leftPadding: 20
topPadding: 0
bottomPadding: 0
verticalAlignment: Text.AlignVCenter
Layout.fillWidth: true
Layout.preferredHeight: parent.height
MouseArea {
anchors.fill: parent
property int lastX: 0
property int lastY: 0
onPressed: {
lastX = mouse.x
lastY = mouse.y
}
onPositionChanged:{
var deltaX = mouse.x - lastX
dialog.x += deltaX
lastX = mouse.x - deltaX
var deltaY = mouse.y - lastY
dialog.y += deltaY
lastY = mouse.y - deltaY
}
}
}
Image {
id: closeImage
source: "qrc:/Icons/close.svg"
sourceSize.width: 36
sourceSize.height: 36
Layout.rightMargin: 10
MouseArea {
anchors.fill: parent
propagateComposedEvents: true
onPressed: {
closeImage.sourceSize.width = 32
closeImage.sourceSize.height = 32
}
onReleased: {
closeImage.sourceSize.width = 36
closeImage.sourceSize.height = 36
}
onClicked: {
root.close()
}
}
}
}
}

View File

@@ -20,3 +20,5 @@ TabButton 1.0 TabButton.qml
TabBar 1.0 TabBar.qml TabBar 1.0 TabBar.qml
ChartView 1.0 ChartView.qml ChartView 1.0 ChartView.qml
TextArea 1.0 TextArea.qml TextArea 1.0 TextArea.qml
DialogHeader 1.0 DialogHeader.qml
DialogBackground 1.0 DialogBackground.qml

1
qml/Icons/close.svg Normal file
View File

@@ -0,0 +1 @@
<svg width="24px" height="24px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><g data-name="Layer 2"><g data-name="close"><rect width="24" height="24" transform="rotate(180 12 12)" opacity="0"/><path d="M13.41 12l4.3-4.29a1 1 0 1 0-1.42-1.42L12 10.59l-4.29-4.3a1 1 0 0 0-1.42 1.42l4.3 4.29-4.3 4.29a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l4.29-4.3 4.29 4.3a1 1 0 0 0 1.42 0 1 1 0 0 0 0-1.42z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 411 B

3
qml/Icons/connection.svg Normal file
View File

@@ -0,0 +1,3 @@
<svg width="24px" height="24px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill="none" stroke="#000" stroke-width="2" d="M10,21 C7.50000053,23.5 5.00000002,23 3,21 C0.999999977,19 0.500000114,16.5 3.00000004,14 C5.49999997,11.5 5.99999998,11 5.99999998,11 L13.0000005,18 C13.0000005,18 12.4999995,18.5 10,21 Z M14.0003207,3 C16.5,0.499999776 19,0.999999776 21.001068,3 C23.002136,5.00000022 23.5,7.49999978 21.001068,10 C18.5021359,12.5000002 18.0007478,13 18.0007478,13 L11,6 C11,6 11.5006414,5.50000022 14.0003207,3 Z M11,9.9999 L8.5,12.4999999 L11,9.9999 Z M14,13 L11.5,15.5 L14,13 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 618 B

9
qml/Icons/info.svg Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
<g transform="translate(0 -1028.4)">
<path d="m22 12a10 10 0 1 1 -20 0 10 10 0 1 1 20 0z" transform="translate(0 1029.4)" fill="#2980b9"/>
<path d="m22 12a10 10 0 1 1 -20 0 10 10 0 1 1 20 0z" transform="translate(0 1028.4)" fill="#3498db"/>
<path d="m11 1035.4v2h2v-2h-2zm-1 4-1 1h2v6h-2v1h1 4 1v-1h-2v-7h-3z" fill="#2980b9"/>
<path d="m11 6v2h2v-2h-2zm-1 4l-1 1h2v6h-2v1h1 4 1v-1h-2v-7h-3z" transform="translate(0 1028.4)" fill="#ecf0f1"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 762 B

8
qml/Icons/warning.svg Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
<g transform="translate(0 -1028.4)">
<path d="m12 1031.4c-0.985-0.1-1.715 0.7-2.0313 1.6-2.6053 5.2-5.2088 10.4-7.8125 15.6-0.6178 1.3 0.5802 2.9 2 2.8h7.8438 7.844c1.42 0.1 2.618-1.5 2-2.8-2.604-5.2-5.207-10.4-7.813-15.6-0.316-0.9-1.046-1.7-2.031-1.6z" fill="#f39c12"/>
<path d="m12 2c-0.985-0.0372-1.715 0.7682-2.0312 1.625-2.6054 5.2106-5.2089 10.418-7.8126 15.625-0.6178 1.307 0.5802 2.919 2 2.75 2.6101-0.003 5.2337-0.001 7.8438 0 2.61-0.001 5.234-0.003 7.844 0 1.42 0.169 2.618-1.443 2-2.75-2.604-5.207-5.207-10.414-7.813-15.625-0.316-0.8568-1.046-1.6622-2.031-1.625z" transform="translate(0 1028.4)" fill="#f1c40f"/>
<path d="m12 8c-0.552 0-1 0.4477-1 1l0.5 7h1l0.5-7c0-0.5523-0.448-1-1-1zm0 9c-0.552 0-1 0.448-1 1s0.448 1 1 1 1-0.448 1-1-0.448-1-1-1z" transform="translate(0 1028.4)" fill="#34495e"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,6 +1,7 @@
import QtQuick 2.12 import QtQuick 2.12
import QtQuick.Controls 2.12 import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12 import QtQuick.Layouts 1.12
import QtGraphicalEffects 1.0
import Controls 1.0 as Controls import Controls 1.0 as Controls
import Screens 1.0 as Screens import Screens 1.0 as Screens
@@ -106,7 +107,7 @@ ApplicationWindow {
{"text": qsTr("Visualization"), "icon": "qrc:/Icons/visualization.svg"}, {"text": qsTr("Visualization"), "icon": "qrc:/Icons/visualization.svg"},
{"text": qsTr("History"), "icon": "qrc:/Icons/history.svg"}, {"text": qsTr("History"), "icon": "qrc:/Icons/history.svg"},
{"text": qsTr("BMS service"), "icon": "qrc:/Icons/bms-service.svg"}, {"text": qsTr("BMS service"), "icon": "qrc:/Icons/bms-service.svg"},
{"text": qsTr("Exit"), "icon": "qrc:/Icons/exit.svg"}, // {"text": qsTr("Exit"), "icon": "qrc:/Icons/exit.svg"},
] ]
delegate: ItemDelegate { delegate: ItemDelegate {
@@ -151,6 +152,55 @@ ApplicationWindow {
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
} }
ItemDelegate {
leftPadding: pane.minimized ? 0 : 40
onClicked: connectionDialog.open()
contentItem: RowLayout {
spacing: pane.minimized ? 0 : 25
height: 50
Item {
visible: pane.minimized
Layout.fillWidth: true
}
Image {
source: "qrc:/Icons/connection.svg"
sourceSize.width: 24
sourceSize.height: 24
Layout.alignment: Qt.AlignCenter
ColorOverlay {
anchors.fill: parent
source: parent
color: "#FFFFFF"
}
}
Label {
text: qsTr("Connection")
font.weight: Font.Bold
color: Palette.alternativeTextColor
visible: !pane.minimized
}
Item {
Layout.fillWidth: true
}
}
background: Rectangle {
color: parent.down ? Palette.pressedButtonColor : Palette.buttonColor
}
Layout.fillWidth: true
}
Item {
Layout.preferredHeight: 40
}
} }
PropertyAnimation { PropertyAnimation {
@@ -174,7 +224,7 @@ ApplicationWindow {
qsTr("Cell monitor"), qsTr("Cell monitor"),
qsTr("BMS settings"), qsTr("BMS settings"),
qsTr("Visualization"), qsTr("Visualization"),
qsTr("History"), qsTr("Information output"),
qsTr("Terminal"), qsTr("Terminal"),
qsTr("Exit"), qsTr("Exit"),
] ]
@@ -189,6 +239,45 @@ ApplicationWindow {
Layout.fillWidth: true Layout.fillWidth: true
} }
RowLayout {
ColumnLayout {
RowLayout {
Controls.ContentLabel {
id: connectionStatusLabel
text: qsTr("Disconnected")
Layout.alignment: Qt.AlignRight
}
Controls.AvailabilityIndicator {
id: connectionStatusIndicator
enabled: false
Layout.alignment: Qt.AlignCenter
}
Controls.ContentLabel {
text: qsTr("V1.3")
Layout.alignment: Qt.AlignRight
}
Layout.alignment: Qt.AlignRight
}
RowLayout {
Controls.ContentLabel {
text: qsTr("Serial number") + ":"
Layout.alignment: Qt.AlignRight
}
Controls.SubtitleLabel {
text: qsTr("9999997")
Layout.alignment: Qt.AlignRight
}
Layout.alignment: Qt.AlignRight
}
}
}
Layout.leftMargin: 45 Layout.leftMargin: 45
Layout.rightMargin: 45 Layout.rightMargin: 45
} }
@@ -215,7 +304,7 @@ ApplicationWindow {
Screens.VisualizationScreen { Screens.VisualizationScreen {
} }
Screens.BmsSettingsScreen { Screens.DebugInformationScreen {
} }
Screens.BmsServiceScreen { Screens.BmsServiceScreen {
@@ -224,8 +313,84 @@ ApplicationWindow {
} }
} }
Screens.ConnectionScreen { Connections {
id: connectionScreen target: BmsInterface
onPortConnectedChanged: {
connectionStatusLabel.text = BmsInterface.isPortConnected() ? qsTr("Connected") : qsTr("Disconnected")
connectionStatusIndicator.enabled = BmsInterface.isPortConnected()
}
onMessageDialog: {
if (!messageDialog.visible) {
messageDialog.title = title
messageDialog.description = msg
messageDialog.good = isGood
messageDialog.open()
} else {
messageDialog.queue.push({"title": title, "description": msg, "good": isGood})
}
}
onStatusMessage: {
for (var i = 0; i < statusPopup.filteredStatuses.length; ++i) {
if (msg === statusPopup.filteredStatuses[i]) {
return
}
}
if (!statusPopup.visible) {
statusPopup.text = msg
statusPopup.good = isGood
statusPopup.open()
hideStatusTimer.start()
} else {
if (statusPopup.text !== msg) {
statusPopup.queue.push({"text": msg, "good": isGood})
}
}
}
}
Screens.ConnectionDialog {
id: connectionDialog
}
Screens.MessageDialog {
id: messageDialog
property var queue: []
onClosed: {
if (queue.length > 0) {
var message = queue.pop()
messageDialog.title = message.title
messageDialog.description = message.description
messageDialog.good = message.good
messageDialog.open()
}
}
}
Screens.StatusPopup {
id: statusPopup
property var filteredStatuses: [qsTr("BMS configuration updated")]
property var queue: []
onClosed: {
if (queue.length > 0) {
var message = queue.pop()
statusPopup.text = message.text
statusPopup.good = message.good
statusPopup.open()
hideStatusTimer.start()
}
}
}
Timer {
id: hideStatusTimer
interval: 3000
onTriggered: statusPopup.close()
} }
background: Rectangle { background: Rectangle {
@@ -233,9 +398,9 @@ ApplicationWindow {
} }
Component.onCompleted: { Component.onCompleted: {
BmsInterface.bmsConfig().loadParamsXml("://res/config.xml") // BmsInterface.bmsConfig().loadParamsXml("://res/config.xml")
BmsInterface.infoConfig().loadParamsXml("://res/info.xml") // BmsInterface.infoConfig().loadParamsXml("://res/info.xml")
connectionScreen.open() connectionDialog.open()
} }
} }

View File

@@ -0,0 +1,86 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import Controls 1.0 as Controls
import Cubo 1.0
import Utils 1.0
Dialog {
id: root
title: qsTr("Connection screen")
width: 400
height: 320
modal: true
closePolicy: Popup.CloseOnEscape
header: Controls.DialogHeader {
dialog: root
}
contentItem: Rectangle {
color: Palette.screenBackgroundColor
ColumnLayout {
anchors.fill: parent
anchors.leftMargin: 45
anchors.rightMargin: 45
spacing: 20
Item {
Layout.fillHeight: true
}
Label {
text: qsTr("Select serial port")
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
}
Controls.ComboBox {
id: serialBox
model: BmsInterface.serialPortNames()
Layout.fillWidth: true
Layout.alignment: Qt.AlignCenter
}
Controls.Button {
id: connectButton
text: qsTr("Connect")
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
onClicked: {
if (BmsInterface.isPortConnected()) {
BmsInterface.disconnectPort()
} else {
var currentPort = BmsInterface.serialPortNames()[serialBox.currentIndex]
if (BmsInterface.connectSerial(currentPort)) {
root.close()
}
}
}
}
Item {
Layout.fillHeight: true
}
}
}
background: Controls.DialogBackground {}
onVisibleChanged: {
x = Qt.binding(function() { return (parent.width - width) / 2})
y = Qt.binding(function() { return (parent.height - height) / 2})
serialBox.model = BmsInterface.serialPortNames()
}
Connections {
target: BmsInterface
onPortConnectedChanged: {
connectButton.text = BmsInterface.isPortConnected() ? qsTr("Disconnect") : qsTr("Connect")
serialBox.enabled = !BmsInterface.isPortConnected()
}
}
}

View File

@@ -1,49 +0,0 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.12
import Controls 1.0 as Controls
import Cubo 1.0
import Utils 1.0
Dialog {
id: root
title: qsTr("Connection Screen")
width: 300
height: 300
standardButtons: Dialog.NoButton
contentItem: Rectangle {
color: Palette.screenBackgroundColor
ColumnLayout {
anchors.fill: parent
anchors.margins: 45
Label {
text: qsTr("Select serial port")
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
}
Controls.ComboBox {
id: serialBox
model: BmsInterface.serialPortNames()
Layout.fillWidth: true
Layout.alignment: Qt.AlignCenter
}
Controls.Button {
text: qsTr("Connect")
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
onClicked: {
var currentPort = BmsInterface.serialPortNames()[serialBox.currentIndex]
if (BmsInterface.connectSerial(currentPort))
root.close()
}
}
}
}
}

View File

@@ -0,0 +1,35 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import Controls 1.0 as Controls
import Cubo 1.0
ColumnLayout {
spacing: 20
Controls.Frame {
ScrollView {
anchors.fill: parent
Controls.TextArea {
id: outputArea
}
}
Layout.fillWidth: true
Layout.fillHeight: true
}
Controls.Button {
text: qsTr("Clear")
Layout.preferredWidth: 120
onClicked: outputArea.clear()
}
Connections {
target: BmsInterface
onStatusMessage: {
outputArea.append(msg)
}
}
}

View File

@@ -0,0 +1,65 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import Controls 1.0 as Controls
import Utils 1.0
Dialog {
id: root
width: 500
height: 350
modal: true
closePolicy: Popup.CloseOnEscape
bottomPadding: 0
property string description: ""
property bool good: true
header: Controls.DialogHeader {
dialog: root
}
contentItem: ColumnLayout {
spacing: 20
RowLayout {
spacing: 20
Image {
source: good ? "qrc:/Icons/info.svg" : "qrc:/Icons/warning.svg"
sourceSize.width: 52
sourceSize.height: 48
}
Controls.SubtitleLabel {
text: root.description
maximumLineCount: 10
wrapMode: Text.Wrap
Layout.fillWidth: true
}
Layout.fillWidth: true
Layout.fillHeight: true
}
RowLayout {
Controls.Button {
text: qsTr("Ok")
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: 100
onClicked: root.close()
}
Layout.alignment: Qt.AlignCenter
Layout.fillWidth: true
}
}
background: Controls.DialogBackground {}
onVisibleChanged: {
x = Qt.binding(function() { return (parent.width - width) / 2})
y = Qt.binding(function() { return (parent.height - height) / 2})
}
}

View File

@@ -0,0 +1,48 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
import Controls 1.0 as Controls
import Utils 1.0
Popup {
id: root
x: (parent.width - width) / 2
y: parent.height - height - 20
closePolicy: Popup.NoAutoClose
property string text: ""
property bool good: true
Controls.SubtitleLabel {
id: label
text: root.text
maximumLineCount: 10
wrapMode: Text.Wrap
color: Palette.alternativeTextColor
}
background: Rectangle {
radius: 6
color: good ? Palette.alternativeBackgroundColor : Palette.invalidColor
border.width: 1
border.color: Palette.borderColor
}
enter: Transition {
NumberAnimation {
property: "opacity"
from: 0.0
to: 1.0
duration: 300
}
}
exit: Transition {
NumberAnimation {
property: "opacity"
from: 1.0
to: 0.0
duration: 2500
}
}
}

View File

@@ -1,7 +1,10 @@
module Screens module Screens
ConnectionScreen 1.0 ConnectionScreen.qml
AkbMonitorScreen 1.0 AkbMonitorScreen.qml AkbMonitorScreen 1.0 AkbMonitorScreen.qml
CellMonitorScreen 1.0 CellMonitorScreen.qml CellMonitorScreen 1.0 CellMonitorScreen.qml
BmsSettingsScreen 1.0 BmsSettingsScreen.qml BmsSettingsScreen 1.0 BmsSettingsScreen.qml
VisualizationScreen 1.0 VisualizationScreen.qml VisualizationScreen 1.0 VisualizationScreen.qml
BmsServiceScreen 1.0 BmsServiceScreen.qml BmsServiceScreen 1.0 BmsServiceScreen.qml
DebugInformationScreen 1.0 DebugInformationScreen.qml
ConnectionDialog 1.0 ConnectionDialog.qml
MessageDialog 1.0 MessageDialog.qml
StatusPopup 1.0 StatusPopup.qml

View File

@@ -21,4 +21,6 @@ QtObject {
property color outlineButtonColor: "#F7F8FC" property color outlineButtonColor: "#F7F8FC"
property color hoveredButtonColor: "#03AC61" property color hoveredButtonColor: "#03AC61"
property color pressedButtonColor: "#057845" property color pressedButtonColor: "#057845"
property color invalidColor: "#A31C00"
} }

View File

@@ -10,5 +10,9 @@
<file>Icons/history.svg</file> <file>Icons/history.svg</file>
<file>Icons/visualization.svg</file> <file>Icons/visualization.svg</file>
<file>Icons/check-indicator.svg</file> <file>Icons/check-indicator.svg</file>
<file>Icons/connection.svg</file>
<file>Icons/close.svg</file>
<file>Icons/info.svg</file>
<file>Icons/warning.svg</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@@ -1,7 +1,7 @@
<RCC> <RCC>
<qresource prefix="/"> <qresource prefix="/">
<file>MainWindow.qml</file> <file>MainWindow.qml</file>
<file>Screens/ConnectionScreen.qml</file> <file>Screens/ConnectionDialog.qml</file>
<file>Screens/AkbMonitorScreen.qml</file> <file>Screens/AkbMonitorScreen.qml</file>
<file>Screens/CellMonitorScreen.qml</file> <file>Screens/CellMonitorScreen.qml</file>
<file>Controls/ComboBox.qml</file> <file>Controls/ComboBox.qml</file>
@@ -32,5 +32,10 @@
<file>Controls/ChartView.qml</file> <file>Controls/ChartView.qml</file>
<file>Screens/BmsServiceScreen.qml</file> <file>Screens/BmsServiceScreen.qml</file>
<file>Controls/TextArea.qml</file> <file>Controls/TextArea.qml</file>
<file>Screens/DebugInformationScreen.qml</file>
<file>Controls/DialogHeader.qml</file>
<file>Controls/DialogBackground.qml</file>
<file>Screens/MessageDialog.qml</file>
<file>Screens/StatusPopup.qml</file>
</qresource> </qresource>
</RCC> </RCC>