First commit

This commit is contained in:
spiros
2024-05-29 03:49:25 +03:00
parent 550b26a41e
commit b36b55f575
10 changed files with 916 additions and 0 deletions

32
.gitignore vendored Normal file
View File

@@ -0,0 +1,32 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app

73
CMakeLists.txt Normal file
View File

@@ -0,0 +1,73 @@
cmake_minimum_required(VERSION 3.5)
project(KDiskInfo VERSION 0.1 LANGUAGES CXX)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
set(PROJECT_SOURCES
main.cpp
mainwindow.cpp
mainwindow.h
mainwindow.ui
)
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(KDiskInfo
MANUAL_FINALIZATION
${PROJECT_SOURCES}
mainwindow.ui
statusdot.h statusdot.cpp
custombutton.h custombutton.cpp
)
# Define target properties for Android with Qt 6 as:
# set_property(TARGET KDiskInfo APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
# ${CMAKE_CURRENT_SOURCE_DIR}/android)
# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
else()
if(ANDROID)
add_library(KDiskInfo SHARED
${PROJECT_SOURCES}
)
# Define properties for Android with Qt 5 after find_package() calls as:
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
else()
add_executable(KDiskInfo
${PROJECT_SOURCES}
)
endif()
endif()
target_link_libraries(KDiskInfo PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
# If you are developing for iOS or macOS you should consider setting an
# explicit, fixed bundle identifier manually though.
if(${QT_VERSION} VERSION_LESS 6.1.0)
set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.KDiskInfo)
endif()
set_target_properties(KDiskInfo PROPERTIES
${BUNDLE_ID_OPTION}
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE TRUE
)
include(GNUInstallDirs)
install(TARGETS KDiskInfo
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
if(QT_VERSION_MAJOR EQUAL 6)
qt_finalize_executable(KDiskInfo)
endif()

22
custombutton.cpp Normal file
View File

@@ -0,0 +1,22 @@
#include "custombutton.h"
CustomButton::CustomButton(const QString &text1, const QString &text2, const QString &text3, const QColor &lineColor, QWidget *parent)
: QPushButton(parent), text1(text1), text2(text2), text3(text3), lineColor(lineColor) {
setMinimumHeight(60);
setMinimumWidth(100);
}
void CustomButton::paintEvent(QPaintEvent *event) {
QPushButton::paintEvent(event);
QPainter painter(this);
QPalette pal = palette();
QColor textColor = pal.color(QPalette::ButtonText);
QPen pen(lineColor, 5);
painter.setPen(pen);
painter.drawLine(10, 9, 10, 50);
QRect rect(20, 0, width() - 25, height());
painter.setPen(textColor);
painter.drawText(rect, Qt::AlignVCenter | Qt::AlignLeft, text1 + "\n" + text2 + "\n" + text3);
}

23
custombutton.h Normal file
View File

@@ -0,0 +1,23 @@
#ifndef CUSTOMBUTTON_H
#define CUSTOMBUTTON_H
#include <QPushButton>
#include <QPainter>
class CustomButton : public QPushButton {
Q_OBJECT
public:
CustomButton(const QString &text1, const QString &text2, const QString &text3, const QColor &lineColor, QWidget *parent = nullptr);
protected:
void paintEvent(QPaintEvent *event) override;
private:
QString text1;
QString text2;
QString text3;
QColor lineColor;
};
#endif

11
main.cpp Normal file
View File

@@ -0,0 +1,11 @@
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

348
mainwindow.cpp Normal file
View File

@@ -0,0 +1,348 @@
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
buttonGroup = new QButtonGroup(this);
buttonGroup->setExclusive(true);
horizontalLayout = ui->horizontalLayout;
diskName = qobject_cast<QLabel *>(ui->centralwidget->findChild<QLabel*>("diskName"));
temperatureValue = qobject_cast<QLabel *>(ui->centralwidget->findChild<QLabel*>("temperatureValueLabel"));
healthStatusValue = qobject_cast<QLabel *>(ui->centralwidget->findChild<QLabel*>("healthStatusValueLabel"));
firmwareLineEdit = qobject_cast<QLineEdit *>(ui->centralwidget->findChild<QLineEdit*>("firmwareLineEdit"));
serialNumberLineEdit = qobject_cast<QLineEdit *>(ui->centralwidget->findChild<QLineEdit*>("serialNumberLineEdit"));
typeLineEdit = qobject_cast<QLineEdit *>(ui->centralwidget->findChild<QLineEdit*>("typeLineEdit"));
protocolLineEdit = qobject_cast<QLineEdit *>(ui->centralwidget->findChild<QLineEdit*>("protocolLineEdit"));
deviceNodeLineEdit = qobject_cast<QLineEdit *>(ui->centralwidget->findChild<QLineEdit*>("deviceNodeLineEdit"));
totalReadsLineEdit = qobject_cast<QLineEdit *>(ui->centralwidget->findChild<QLineEdit*>("totalReadsLineEdit"));
totalWritesLineEdit = qobject_cast<QLineEdit *>(ui->centralwidget->findChild<QLineEdit*>("totalWritesLineEdit"));
rotationRateLineEdit = qobject_cast<QLineEdit *>(ui->centralwidget->findChild<QLineEdit*>("rotationRateLineEdit"));
powerOnCountLineEdit = qobject_cast<QLineEdit *>(ui->centralwidget->findChild<QLineEdit*>("powerOnCountLineEdit"));
powerOnHoursLineEdit = qobject_cast<QLineEdit *>(ui->centralwidget->findChild<QLineEdit*>("powerOnHoursLineEdit"));
tableWidget = qobject_cast<QTableWidget *>(ui->centralwidget->findChild<QTableWidget*>("dataTable"));;
serialNumberLineEdit->setEchoMode(QLineEdit::Password);
QAction *toggleEchoModeAction = serialNumberLineEdit->addAction(QIcon::fromTheme(QStringLiteral("visibility")), QLineEdit::TrailingPosition);
connect(toggleEchoModeAction, &QAction::triggered, [=]() {
if (serialNumberLineEdit->echoMode() == QLineEdit::Password) {
serialNumberLineEdit->setEchoMode(QLineEdit::Normal);
toggleEchoModeAction->setIcon(QIcon::fromTheme(QStringLiteral("hint")));
} else if (serialNumberLineEdit->echoMode() == QLineEdit::Normal) {
serialNumberLineEdit->setEchoMode(QLineEdit::Password);
toggleEchoModeAction->setIcon(QIcon::fromTheme(QStringLiteral("visibility")));
}
});
scanDevices();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::scanDevices()
{
QString output = getSmartctlOutput({"--scan", "--json"}, false);
QJsonDocument doc = QJsonDocument::fromJson(output.toUtf8());
QJsonObject jsonObj = doc.object();
QJsonArray devices = jsonObj["devices"].toArray();
QJsonObject globalObj;
QString globalHealth;
bool firstTime = true;
for (const QJsonValue &value : devices) {
QJsonObject device = value.toObject();
QString deviceName = device["name"].toString();
QString allOutput = getSmartctlOutput({"--all", "--json", deviceName}, true);
QJsonDocument localDoc = QJsonDocument::fromJson(allOutput.toUtf8());
QJsonObject localObj = localDoc.object();
QJsonArray attributes = localObj["ata_smart_attributes"].toObject()["table"].toArray();
QString temperature = "N/A";
bool healthPassed = localObj["smart_status"].toObject()["passed"].toBool();
bool caution = false;
QString health;
QColor healthColor;
bool isNvme = false;
QString protocol = localObj["device"].toObject()["protocol"].toString();
if (protocol == "NVMe") {
isNvme = true;
}
for (const QJsonValue &attr : attributes) { // Need different logic for NVMe
QJsonObject attrObj = attr.toObject();
if (attrObj["id"] == 194 && !isNvme) {
QString raw = attrObj["raw"].toObject()["string"].toString();
int spaceIndex = raw.indexOf(' ');
if (spaceIndex != -1) {
raw = raw.left(spaceIndex);
}
int tempInt = raw.toInt();
temperature = QString::number(tempInt) + " °C";
} else if (!isNvme && (attrObj["id"] == 5 || attrObj["id"] == 197 || attrObj["id"] == 198) && (attrObj["raw"].toObject()["value"].toInt() != 0)) {
caution = true;
}
}
if (healthPassed && !caution) {
health = "Good";
healthColor = Qt::green;
} else if (healthPassed && caution) {
health = "Caution";
healthColor = Qt::yellow;
} else {
health = "Bad";
healthColor = Qt::red;
}
CustomButton *button = new CustomButton(health, deviceName, temperature, healthColor, this);
buttonGroup->addButton(button);
horizontalLayout->addWidget(button);
button->setCheckable(true);
button->setAutoExclusive(true);
connect(button, &QPushButton::clicked, this, [=]() {
populateWindow(localObj, health);
});
if (firstTime) {
globalObj = localObj;
globalHealth = health;
button->setChecked(true);
}
firstTime = false;
}
horizontalLayout->addStretch();
populateWindow(globalObj, globalHealth);
}
void MainWindow::populateWindow(const QJsonObject &localObj, const QString &health)
{
QJsonArray attributes = localObj["ata_smart_attributes"].toObject()["table"].toArray();
QString modelName = localObj["model_name"].toString();
QString firmwareVersion = localObj["firmware_version"].toString();
float userCapacityGB = localObj.value("user_capacity").toObject().value("bytes").toDouble() / 1e9;
QString userCapacityString = QString::number(static_cast<int>(userCapacityGB)) + "." + QString::number(static_cast<int>((userCapacityGB - static_cast<int>(userCapacityGB)) * 10)) + " GB";
QString totalReads = "----";
QString totalWrites = "----";
QString serialNumber = localObj["serial_number"].toString();
QJsonObject deviceObj = localObj["device"].toObject();
QString protocol = deviceObj["protocol"].toString();
QString type = deviceObj["type"].toString();
QString name = deviceObj["name"].toString();
int tempInt = 0;
bool isNvme = false;
if (protocol == "NVMe") {
isNvme = true;
}
diskName->setText("<html><head/><body><p><span style='font-size:14pt; font-weight:bold;'>" + modelName + " " + userCapacityString + "</span></p></body></html>");
firmwareLineEdit->setText(firmwareVersion);
serialNumberLineEdit->setText(serialNumber);
typeLineEdit->setText(type);
protocolLineEdit->setText(protocol);
deviceNodeLineEdit->setText(name);
int rotationRateInt = localObj["rotation_rate"].toInt(-1);
QString rotationRate;
if (rotationRateInt > 0) {
rotationRate = QString::number(rotationRateInt);
} else if (rotationRateInt == 0) {
rotationRate = "---- (SSD)";
} else {
rotationRate = "----";
}
rotationRateLineEdit->setText(rotationRate);
rotationRateLineEdit->setAlignment(Qt::AlignRight);
int powerCycleCountInt = localObj["power_cycle_count"].toInt(-1);
QString powerCycleCount;
if (powerCycleCountInt >= 0) {
powerCycleCount = QString::number(powerCycleCountInt) + " count";
} else {
powerCycleCount = "Unknown";
}
powerOnCountLineEdit->setText(powerCycleCount);
powerOnCountLineEdit->setAlignment(Qt::AlignRight);
int powerOnTimeInt = localObj["power_on_time"].toObject().value("hours").toInt(-1);
QString powerOnTime;
if (powerOnTimeInt >= 0) {
powerOnTime = QString::number(powerOnTimeInt) + " hours";
} else {
powerOnTime = "Unknown";
}
powerOnHoursLineEdit->setText(powerOnTime);
powerOnHoursLineEdit->setAlignment(Qt::AlignRight);
for (const QJsonValue &attr : attributes) { //Need different logic for NVMe
QJsonObject attrObj = attr.toObject();
if (attrObj["id"] == 241 && !isNvme) {
totalWrites = QString::number(attrObj["raw"].toObject()["value"].toInt()) + " GB";
} else if (attrObj["id"] == 242 && !isNvme) {
totalReads = QString::number(attrObj["raw"].toObject()["value"].toInt()) + " GB";
} else if (attrObj["id"] == 194 && !isNvme) {
QString raw = attrObj["raw"].toObject()["string"].toString();
int spaceIndex = raw.indexOf(' ');
if (spaceIndex != -1) {
raw = raw.left(spaceIndex);
}
tempInt = raw.toInt();
}
}
totalReadsLineEdit->setText(totalReads);
totalReadsLineEdit->setAlignment(Qt::AlignRight);
totalWritesLineEdit->setText(totalWrites);
totalWritesLineEdit->setAlignment(Qt::AlignRight);
if (tempInt > 55) {
temperatureValue->setStyleSheet("background-color: " + QColor(Qt::red).name() + ";");
} else if ((tempInt < 55) && (tempInt > 50)){
temperatureValue->setStyleSheet("background-color: " + QColor(Qt::yellow).name() + ";");
} else if (tempInt == 0) {
temperatureValue->setStyleSheet("background-color: " + QColor(Qt::gray).name() + ";");
} else {
temperatureValue->setStyleSheet("background-color: " + QColor(Qt::green).name() + ";");
}
QString labelStyle = "font-size:12pt; font-weight:700; color:black";
if (tempInt > 0) {
temperatureValue->setText("<html><head/><body><p><span style='" + labelStyle +"'>" + QString::number(tempInt) + " °C</span></p></body></html>");
} else {
temperatureValue->setText("<html><head/><body><p><span style='" + labelStyle +"'>N/A</span></p></body></html>");
}
temperatureValue->setAlignment(Qt::AlignCenter);
if (health == "Bad") {
healthStatusValue->setStyleSheet("background-color: " + QColor(Qt::red).name() + ";");
} else if (health == "Caution"){
healthStatusValue->setStyleSheet("background-color: " + QColor(Qt::yellow).name() + ";");
} else {
healthStatusValue->setStyleSheet("background-color: " + QColor(Qt::green).name() + ";");
}
healthStatusValue->setText("<html><head/><body><p><span style='" + labelStyle +"'>" + health + "</span></p></body></html>");
healthStatusValue->setAlignment(Qt::AlignCenter);
if (protocol != "NVMe") {
addSmartAttributesTable(attributes);
}
}
void MainWindow::addSmartAttributesTable(const QJsonArray &attributes)
{
tableWidget->setColumnCount(7);
tableWidget->setHorizontalHeaderLabels({"", "ID", "Attribute Name", "Current", "Worst", "Threshold", "Raw Values"});
tableWidget->verticalHeader()->setVisible(false);
tableWidget->setItemDelegateForColumn(0, new StatusDot(tableWidget));
tableWidget->setRowCount(attributes.size());
int row = 0;
for (const QJsonValue &attr : attributes) {
QJsonObject attrObj = attr.toObject();
QString id = QString("%1").arg(attrObj["id"].toInt(), 2, 16, QChar('0')).toUpper();
QString name = attrObj["name"].toString().replace("_", " ");
int value = attrObj["value"].toInt();
int worst = attrObj["worst"].toInt();
int thresh = attrObj["thresh"].toInt();
QString raw = attrObj["raw"].toObject()["string"].toString();
int spaceIndex = raw.indexOf(' ');
if (spaceIndex != -1) {
raw = raw.left(spaceIndex);
}
raw = QString("%1").arg(raw.toUInt(nullptr), 12, 16, QChar('0')).toUpper();
QColor statusColor;
if ((thresh != 0) && (value < thresh)) {
statusColor = Qt::red;
} else if ((id == "05" || id == "C5" || id == "C6") && (raw != "0")) {
statusColor = Qt::yellow;
} else {
statusColor = Qt::green;
}
QTableWidgetItem *statusItem = new QTableWidgetItem();
statusItem->setBackground(Qt::transparent);
statusItem->setData(Qt::BackgroundRole, QVariant(statusColor));
QTableWidgetItem *idItem = new QTableWidgetItem(id);
idItem->setTextAlignment(Qt::AlignCenter);
QTableWidgetItem *nameItem = new QTableWidgetItem(name);
QTableWidgetItem *valueItem = new QTableWidgetItem(QString::number(value));
valueItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
QTableWidgetItem *worstItem = new QTableWidgetItem(QString::number(worst));
worstItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
QTableWidgetItem *threshItem = new QTableWidgetItem(QString::number(thresh));
threshItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
QTableWidgetItem *rawItem = new QTableWidgetItem(raw);
rawItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
tableWidget->setItem(row, 0, statusItem);
tableWidget->setItem(row, 1, idItem);
tableWidget->setItem(row, 2, nameItem);
tableWidget->setItem(row, 3, valueItem);
tableWidget->setItem(row, 4, worstItem);
tableWidget->setItem(row, 5, threshItem);
tableWidget->setItem(row, 6, rawItem);
++row;
}
tableWidget->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch);
for (int i = 0; i < tableWidget->columnCount(); ++i) {
if (i != 2) {
tableWidget->horizontalHeader()->setSectionResizeMode(i, QHeaderView::ResizeToContents);
}
}
tableWidget->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
}
QString MainWindow::getSmartctlOutput(const QStringList &arguments, bool root)
{
QProcess process;
QString command;
QStringList commandArgs;
if(root) {
command = "pkexec";
commandArgs = {"smartctl"};
} else {
command = "smartctl";
commandArgs = {};
}
commandArgs.append(arguments);
process.start(command, commandArgs);
process.waitForFinished();
return process.readAllStandardOutput();
}

44
mainwindow.h Normal file
View File

@@ -0,0 +1,44 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QProcess>
#include <QWidget>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QButtonGroup>
#include "statusdot.h"
#include "custombutton.h"
#include "./ui_mainwindow.h"
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
QButtonGroup *buttonGroup;
QHBoxLayout *horizontalLayout;
QLabel *diskName, *temperatureValue, *healthStatusValue;
QLineEdit *firmwareLineEdit, *serialNumberLineEdit, *typeLineEdit, *protocolLineEdit, *deviceNodeLineEdit;
QLineEdit *totalReadsLineEdit, *totalWritesLineEdit, *rotationRateLineEdit, *powerOnCountLineEdit, *powerOnHoursLineEdit;
QTableWidget *tableWidget;
void scanDevices();
void populateWindow(const QJsonObject &tempObj, const QString &health);
void addSmartAttributesTable(const QJsonArray &attributes);
QString getSmartctlOutput(const QStringList &arguments, bool root);
};
#endif

326
mainwindow.ui Normal file
View File

@@ -0,0 +1,326 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="windowModality">
<enum>Qt::WindowModality::NonModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>KDiskInfo</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QHBoxLayout" name="disks">
<item>
<layout class="QHBoxLayout" name="horizontalLayout"/>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="navigation">
<item>
<widget class="QPushButton" name="previousButton">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="QIcon::ThemeIcon::GoPrevious"/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="diskName">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:14pt; font-weight:700;&quot;&gt;Hard Drive Name&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="nextButton">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="QIcon::ThemeIcon::GoNext"/>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="info">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="healthStatusLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;Health Status&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="healthStatusValueLabel">
<property name="styleSheet">
<string notr="true">background-color: rgb(0, 255, 0);</string>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:12pt; font-weight:700; color:#000000;&quot;&gt;Good&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="temperatureLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;Temperature&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="temperatureValueLabel">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>50</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(0, 255, 0);</string>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:12pt; font-weight:700; color:#000000;&quot;&gt;23° C&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="firmwareLabel">
<property name="text">
<string>Firmware</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="firmwareLineEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="serialNumberLabel">
<property name="text">
<string>Serial Number</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="serialNumberLineEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="protocolLabel">
<property name="text">
<string>Protocol</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="protocolLineEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="deviceNodeLabel">
<property name="text">
<string>Device Node</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="deviceNodeLineEdit">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string/>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="typeLabel">
<property name="text">
<string>Type</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="typeLineEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="totalReadsLabel">
<property name="text">
<string>Total Host Reads</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="totalReadsLineEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="totalWritesLabel">
<property name="text">
<string>Total Host Writes</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="totalWritesLineEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="rotationRateLabel">
<property name="text">
<string>Rotation Rate</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="rotationRateLineEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="powerOnCountLabel">
<property name="text">
<string>Power On Count</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="powerOnCountLineEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="powerOnHoursLabel">
<property name="text">
<string>Power On Hours</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="powerOnHoursLineEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QTableWidget" name="dataTable">
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<property name="showGrid">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

17
statusdot.cpp Normal file
View File

@@ -0,0 +1,17 @@
#include "statusdot.h"
void StatusDot::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
int dotSize = qMin(opt.rect.width(), opt.rect.height()) * 0.5;
painter->save();
painter->setRenderHint(QPainter::Antialiasing, true);
QRect dotRect(opt.rect.center().x() - dotSize / 2, opt.rect.center().y() - dotSize / 2, dotSize, dotSize);
QColor color = QColor(index.data(Qt::BackgroundRole).value<QColor>());
painter->setBrush(color);
painter->setPen(Qt::NoPen);
painter->drawEllipse(dotRect);
painter->restore();
}

20
statusdot.h Normal file
View File

@@ -0,0 +1,20 @@
#ifndef STATUSDOT_H
#define STATUSDOT_H
#include <QPainter>
#include <QStyleOptionViewItem>
#include <QStyledItemDelegate>
class StatusDot : public QStyledItemDelegate
{
public:
StatusDot(QObject *parent = nullptr)
: QStyledItemDelegate(parent)
{}
void paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
};
#endif