mirror of
https://github.com/Lurkki14/tuxclocker.git
synced 2025-02-25 18:55:24 -06:00
Merge 5b421550d1
into 3da3b425f6
This commit is contained in:
commit
b9f3c69df6
@ -1,5 +1,5 @@
|
||||
qt5 = import('qt5')
|
||||
qt5_dep = dependency('qt5', modules: ['DBus', 'Charts', 'Widgets'])
|
||||
qt5_dep = dependency('qt5', modules: ['DBus', 'Charts', 'Widgets', 'PrintSupport'])
|
||||
|
||||
# signals2
|
||||
boost_dep = dependency('boost')
|
||||
@ -9,6 +9,8 @@ moc_files = qt5.preprocess(moc_headers: ['MainWindow.hpp',
|
||||
'data/AssignableItem.hpp',
|
||||
'data/AssignableProxy.hpp',
|
||||
'data/DeviceModel.hpp',
|
||||
'monitor/Monitor.hpp',
|
||||
'monitor/qcustomplot.h',
|
||||
'data/DynamicReadableConnection.hpp',
|
||||
'data/DynamicReadableProxy.hpp',
|
||||
'widgets/AbstractAssignableEditor.hpp',
|
||||
@ -28,6 +30,10 @@ sources = ['main.cpp',
|
||||
'data/DynamicReadableProxy.cpp',
|
||||
'widgets/DeviceBrowser.cpp',
|
||||
'widgets/DeviceTreeView.cpp',
|
||||
'monitor/Monitor.cpp',
|
||||
'monitor/Metric.cpp',
|
||||
'monitor/QColorProvider.cpp',
|
||||
'monitor/qcustomplot.cpp',
|
||||
'widgets/DragChartView.cpp',
|
||||
'widgets/EnumEditor.cpp',
|
||||
'widgets/FlagEditor.cpp',
|
||||
|
92
src/tuxclocker-qt/monitor/Metric.cpp
Normal file
92
src/tuxclocker-qt/monitor/Metric.cpp
Normal file
@ -0,0 +1,92 @@
|
||||
#include "Metric.hpp"
|
||||
|
||||
#include <QtDBus/QtDBus>
|
||||
#include <QtDBus/QDBusConnection>
|
||||
#include <QtDBus/QDBusVariant>
|
||||
#include <QtDBus/QDBusInterface>
|
||||
#include <QtDBus/QDBusArgument>
|
||||
#include <QtDBus/QDBusReply>
|
||||
|
||||
#include <thread>
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct BooleanValue
|
||||
{
|
||||
bool my_bool;
|
||||
QDBusVariant val;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(BooleanValue);
|
||||
|
||||
// Marshall the MyStructure data into a D-Bus argument
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const BooleanValue &mystruct)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument << mystruct.my_bool << mystruct.val;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
// Retrieve the MyStructure data from the D-Bus argument
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, BooleanValue &mystruct)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument >> mystruct.my_bool >> mystruct.val;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
Metric::Metric(QString dbus_obj_path) : dbus_obj_path(dbus_obj_path) {
|
||||
qDBusRegisterMetaType<BooleanValue>();
|
||||
|
||||
dynamic_readable_interface = new QDBusInterface(
|
||||
"org.tuxclocker",
|
||||
dbus_obj_path,
|
||||
"org.tuxclocker.DynamicReadable",
|
||||
QDBusConnection::systemBus()
|
||||
);
|
||||
|
||||
if(!dynamic_readable_interface->isValid()){
|
||||
qDebug() << "WARNING: Metric got an invalid dynamic_readable_interface";
|
||||
qDebug() << " ==> path" << dbus_obj_path;
|
||||
}
|
||||
|
||||
fetchName();
|
||||
};
|
||||
|
||||
void Metric::fetchName(){
|
||||
QDBusInterface *node_interface = new QDBusInterface(
|
||||
"org.tuxclocker",
|
||||
dbus_obj_path,
|
||||
"org.tuxclocker.Node",
|
||||
QDBusConnection::systemBus()
|
||||
);
|
||||
|
||||
name = node_interface->property("name").toString();
|
||||
}
|
||||
|
||||
void Metric::fetchValue(){
|
||||
if(dynamic_readable_interface->isValid()){
|
||||
QDBusReply<BooleanValue> reply = dynamic_readable_interface->call("value");
|
||||
|
||||
if(!reply.isValid()){
|
||||
qDebug() << "WARNING: fetchValue had an invalid DBus reply";
|
||||
qDebug() << "error message: " << reply.error().message();
|
||||
qDebug() << "error name: " << reply.error().name();
|
||||
}
|
||||
|
||||
// TODO: actually request double precision on DBus call method signature
|
||||
actualValue = reply.value().val.variant().toDouble();
|
||||
}
|
||||
}
|
||||
|
||||
void Metric::asyncFetchValue(){
|
||||
// TODO: Allow cancellation. There are cases where we may be innecessary blocking
|
||||
// if we delay more time that the next time we are called.
|
||||
futureValue = async(launch::async, &Metric::fetchValue, this);
|
||||
}
|
||||
|
||||
double Metric::value(){
|
||||
return actualValue;
|
||||
}
|
24
src/tuxclocker-qt/monitor/Metric.hpp
Normal file
24
src/tuxclocker-qt/monitor/Metric.hpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include <QtDBus/QDBusInterface>
|
||||
#include <QString>
|
||||
#include <future>
|
||||
|
||||
#ifndef MetricHPP
|
||||
#define MetricHPP
|
||||
|
||||
class Metric {
|
||||
public:
|
||||
QDBusInterface *dynamic_readable_interface;
|
||||
QString dbus_obj_path;
|
||||
QString name;
|
||||
float actualValue = 0;
|
||||
std::future<void> futureValue;
|
||||
|
||||
Metric(QString dbus_readable);
|
||||
|
||||
void fetchName();
|
||||
void fetchValue();
|
||||
void asyncFetchValue();
|
||||
double value();
|
||||
};
|
||||
|
||||
#endif
|
130
src/tuxclocker-qt/monitor/Monitor.cpp
Normal file
130
src/tuxclocker-qt/monitor/Monitor.cpp
Normal file
@ -0,0 +1,130 @@
|
||||
#include "Monitor.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
Monitor::Monitor(QWidget *parent) : QCustomPlot(parent), color_provider() {
|
||||
setMinimumWidth(1700);
|
||||
setMinimumHeight(400);
|
||||
|
||||
setLocale(QLocale(QLocale::English, QLocale::UnitedKingdom));
|
||||
|
||||
setOpenGl(true);
|
||||
//setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
setAntialiasedElements(QCP::AntialiasedElement::aeNone);
|
||||
|
||||
setBackground(QColor(48, 56, 65));
|
||||
//setAutoAddMonitortableToLegend(false);
|
||||
|
||||
xAxis->setRange(0, 60);
|
||||
yAxis->setRange(0, 5000);
|
||||
|
||||
collect_metrics_timer.callOnTimeout(this, &Monitor::collect_metrics);
|
||||
collect_metrics_timer.setInterval(100);
|
||||
|
||||
// With 100 it's low profile like gnome-system-monitor
|
||||
// With 20 it looks nice but may use a bit more cpu (40% of an i7-9750h core)
|
||||
replot_timer.callOnTimeout(this, &Monitor::replot);
|
||||
replot_timer.setInterval(20);
|
||||
|
||||
setupYAxisTick();
|
||||
setupLessImportant();
|
||||
|
||||
QObject::connect(yAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(onRangeChanged(QCPRange)));
|
||||
};
|
||||
|
||||
void Monitor::onRangeChanged(QCPRange range){
|
||||
setupYAxisTick();
|
||||
}
|
||||
|
||||
void Monitor::setupYAxisTick(){
|
||||
QSharedPointer<QCPAxisTickerText> textTicker(new QCPAxisTickerText);
|
||||
|
||||
yAxis->setTicker(textTicker);
|
||||
|
||||
double size = yAxis->range().size();
|
||||
double lower = yAxis->range().lower;
|
||||
|
||||
textTicker->addTick(lower, "0%");
|
||||
textTicker->addTick(lower + 0.2 * size, "20%");
|
||||
textTicker->addTick(lower + 0.4 * size, "40%");
|
||||
textTicker->addTick(lower + 0.6 * size, "60%");
|
||||
textTicker->addTick(lower + 0.8 * size, "80%");
|
||||
textTicker->addTick(yAxis->range().upper, "100%");
|
||||
}
|
||||
|
||||
|
||||
void Monitor::setupLessImportant(){
|
||||
QSharedPointer<QCPAxisTickerTime> timeTicker(new QCPAxisTickerTime);
|
||||
timeTicker->setTimeFormat("%m:%s:%z");
|
||||
timeTicker->setTickCount(8);
|
||||
xAxis->setTicker(timeTicker);
|
||||
|
||||
xAxis->setTickLabelFont(QFont(QFont().family(), 12));
|
||||
yAxis->setTickLabelFont(QFont(QFont().family(), 12));
|
||||
xAxis->setTickLabelColor(QColor(200, 200, 200));
|
||||
yAxis->setTickLabelColor(QColor(200, 200, 200));
|
||||
|
||||
xAxis2->setVisible(true);
|
||||
yAxis2->setVisible(true);
|
||||
xAxis2->setTicks(false);
|
||||
yAxis2->setTicks(false);
|
||||
xAxis2->setTickLabels(false);
|
||||
yAxis2->setTickLabels(false);
|
||||
|
||||
legend->setVisible(true);
|
||||
legend->setBrush(QColor(255, 255, 255, 150));
|
||||
}
|
||||
|
||||
double Monitor::timenow(){
|
||||
return ((double) QDateTime::currentDateTime().time().msecsSinceStartOfDay()) / 1000;
|
||||
}
|
||||
|
||||
void Monitor::collect_initial_metrics(){
|
||||
for(auto metric : metrics){
|
||||
metric->fetchValue();
|
||||
}
|
||||
}
|
||||
|
||||
void Monitor::collect_metrics(){
|
||||
double now = timenow();
|
||||
|
||||
for(int m = 0; m < metrics.count(); m++){
|
||||
graph(m)->data()->add(QCPGraphData (now, metrics[m]->value()));
|
||||
|
||||
// TODO: I dont know how to correctly tell Qt5
|
||||
// to make the dbus call async, for example by using
|
||||
// callWithCallback()
|
||||
metrics[m]->asyncFetchValue();
|
||||
}
|
||||
|
||||
graph()->data()->removeBefore(now - 60);
|
||||
}
|
||||
|
||||
void Monitor::replot(){
|
||||
rescaleAxes();
|
||||
|
||||
double now = timenow();
|
||||
xAxis->setRange(now - 60, now);
|
||||
QCustomPlot::replot();
|
||||
}
|
||||
|
||||
void Monitor::setupGraphs(){
|
||||
clearGraphs();
|
||||
|
||||
for(auto metric : metrics){
|
||||
addGraph();
|
||||
|
||||
graph()->setName(metric->name);
|
||||
graph()->setLineStyle(QCPGraph::lsLine);
|
||||
QPen pen(color_provider.pick());
|
||||
pen.setWidth(1);
|
||||
graph()->setPen(pen);
|
||||
graph()->setAdaptiveSampling(false);
|
||||
}
|
||||
}
|
||||
|
||||
void Monitor::start(){
|
||||
collect_initial_metrics();
|
||||
collect_metrics_timer.start();
|
||||
replot_timer.start();
|
||||
}
|
36
src/tuxclocker-qt/monitor/Monitor.hpp
Normal file
36
src/tuxclocker-qt/monitor/Monitor.hpp
Normal file
@ -0,0 +1,36 @@
|
||||
#include <QTimer>
|
||||
#include <QVector>
|
||||
#include <QWidget>
|
||||
#include <QObject>
|
||||
|
||||
#include "qcustomplot.h"
|
||||
|
||||
#include "QColorProvider.hpp"
|
||||
#include "Metric.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class Monitor : public QCustomPlot {
|
||||
Q_OBJECT
|
||||
public:
|
||||
QTimer collect_metrics_timer;
|
||||
QTimer replot_timer;
|
||||
|
||||
QColorProvider color_provider;
|
||||
QVector<shared_ptr<Metric>> metrics;
|
||||
|
||||
Monitor(QWidget *parent);
|
||||
|
||||
void setupLessImportant();
|
||||
double timenow();
|
||||
void collect_initial_metrics();
|
||||
void collect_metrics();
|
||||
void replot();
|
||||
void setupGraphs();
|
||||
void start();
|
||||
|
||||
void setupYAxisTick();
|
||||
|
||||
public slots:
|
||||
void onRangeChanged(QCPRange range);
|
||||
};
|
11
src/tuxclocker-qt/monitor/QColorProvider.cpp
Normal file
11
src/tuxclocker-qt/monitor/QColorProvider.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
#include "QColorProvider.hpp"
|
||||
|
||||
QColorProvider::QColorProvider(){};
|
||||
|
||||
QColor QColorProvider::pick(){
|
||||
if(current >= colors.count()){
|
||||
current = 0;
|
||||
}
|
||||
|
||||
return colors.at(current++);
|
||||
}
|
35
src/tuxclocker-qt/monitor/QColorProvider.hpp
Normal file
35
src/tuxclocker-qt/monitor/QColorProvider.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#include <QColor>
|
||||
#include <QVector>
|
||||
|
||||
class QColorProvider {
|
||||
public:
|
||||
int current = 0;
|
||||
|
||||
QVector<QColor> colors = {
|
||||
QColor(230, 25, 75, 170), // Red
|
||||
QColor(245, 130, 49, 170), // Orange
|
||||
QColor(255, 255, 25, 170), // Yellow
|
||||
QColor(191, 239, 69, 170), // Lime
|
||||
QColor(60, 180, 75, 170), // Green
|
||||
QColor(66, 212, 244, 170), // Cyan
|
||||
QColor(67, 99, 216, 170), // Navy
|
||||
QColor(240, 50, 230, 170), // Dark Green
|
||||
QColor(145, 30, 180, 170), // Dark Blue
|
||||
QColor(250, 190, 190, 170), // Peach
|
||||
QColor(255, 216, 177, 170), // Tan
|
||||
QColor(255, 250, 200, 170), // Light Blue
|
||||
QColor(128, 0, 255, 170), // Purple
|
||||
QColor(255, 128, 128, 170), // Pink
|
||||
QColor(128, 255, 128, 170), // Light Green
|
||||
QColor(128, 128, 255, 170), // Sky Blue
|
||||
QColor(255, 255, 128, 170), // Light Yellow
|
||||
QColor(255, 128, 255, 170), // Light Pink
|
||||
QColor(128, 255, 255, 170), // Light Turquoise
|
||||
QColor(192, 192, 192, 170) // Gray
|
||||
};
|
||||
|
||||
|
||||
QColorProvider();
|
||||
|
||||
QColor pick();
|
||||
};
|
35529
src/tuxclocker-qt/monitor/qcustomplot.cpp
Normal file
35529
src/tuxclocker-qt/monitor/qcustomplot.cpp
Normal file
File diff suppressed because it is too large
Load Diff
7774
src/tuxclocker-qt/monitor/qcustomplot.h
Normal file
7774
src/tuxclocker-qt/monitor/qcustomplot.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,7 @@
|
||||
#include "DeviceBrowser.hpp"
|
||||
#include "AssignableItemData.hpp"
|
||||
#include "./monitor/Monitor.hpp"
|
||||
#include "./monitor/Metric.hpp"
|
||||
#include "qnamespace.h"
|
||||
|
||||
#include <Globals.hpp>
|
||||
@ -11,6 +13,8 @@
|
||||
#include <QVariant>
|
||||
#include <Settings.hpp>
|
||||
#include <Utils.hpp>
|
||||
#include <QMimeData>
|
||||
#include <QStandardItemModel>
|
||||
|
||||
#define _(String) gettext(String)
|
||||
|
||||
@ -20,6 +24,48 @@ using namespace TuxClocker::Device;
|
||||
Q_DECLARE_METATYPE(AssignableItemData)
|
||||
Q_DECLARE_METATYPE(AssignableProxy *)
|
||||
|
||||
class MonitorDropBox : public QLabel{
|
||||
public:
|
||||
Monitor monitor;
|
||||
QWidget widget;
|
||||
|
||||
MonitorDropBox(QWidget *parent) : QLabel(parent), monitor(nullptr), widget(nullptr){
|
||||
setAcceptDrops(true);
|
||||
setFrameStyle(QFrame::Box);
|
||||
setLineWidth(1);
|
||||
setText("Monitor");
|
||||
setAlignment(Qt::AlignCenter);
|
||||
}
|
||||
|
||||
void setupMonitor(QStringList dbus_paths){
|
||||
monitor.setParent(&widget);
|
||||
widget.show();
|
||||
|
||||
monitor.metrics.clear();
|
||||
|
||||
for(QString dbus_path : dbus_paths){
|
||||
monitor.metrics.append(std::make_shared<Metric>(dbus_path));
|
||||
}
|
||||
|
||||
monitor.setupGraphs();
|
||||
|
||||
monitor.start();
|
||||
}
|
||||
|
||||
void dropEvent(QDropEvent *event) override {
|
||||
QStringList dbus_paths = event->mimeData()->text().split(",");
|
||||
|
||||
if(dbus_paths.count()){
|
||||
setupMonitor(dbus_paths);
|
||||
}
|
||||
}
|
||||
|
||||
void dragEnterEvent(QDragEnterEvent *event) override {
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
DeviceBrowser::DeviceBrowser(DeviceModel &model, QWidget *parent)
|
||||
: QWidget(parent), m_deviceModel(model) {
|
||||
m_layout = new QGridLayout(this);
|
||||
@ -75,9 +121,12 @@ DeviceBrowser::DeviceBrowser(DeviceModel &model, QWidget *parent)
|
||||
Globals::g_mainStack->setCurrentWidget(m_settings);
|
||||
});
|
||||
|
||||
m_layout->addWidget(toolButton, 0, 0);
|
||||
auto monitorDropBox = new MonitorDropBox{this};
|
||||
|
||||
m_layout->addWidget(m_flagLabel, 0, 1, 1, 1, Qt::AlignRight);
|
||||
m_layout->addWidget(toolButton, 0, 0);
|
||||
m_layout->addWidget(monitorDropBox, 0, 1);
|
||||
|
||||
m_layout->addWidget(m_flagLabel, 0, 2, 1, 0, Qt::AlignRight);
|
||||
m_layout->addWidget(m_flagEditor, 0, 2);
|
||||
m_layout->addWidget(m_treeView, 1, 0, 1, 3);
|
||||
m_layout->addWidget(m_apply, 2, 0, 1, 3);
|
||||
|
@ -10,6 +10,8 @@
|
||||
#include <QHeaderView>
|
||||
#include <QSettings>
|
||||
#include <Utils.hpp>
|
||||
#include <QMimeData>
|
||||
#include <QDrag>
|
||||
|
||||
using namespace mpark::patterns;
|
||||
using namespace TuxClocker::Device;
|
||||
@ -26,6 +28,7 @@ DeviceTreeView::DeviceTreeView(QWidget *parent) : QTreeView(parent) {
|
||||
setSortingEnabled(true);
|
||||
setEditTriggers(SelectedClicked | EditKeyPressed);
|
||||
setContextMenuPolicy(Qt::DefaultContextMenu);
|
||||
setDragEnabled(true);
|
||||
|
||||
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
|
||||
@ -69,6 +72,42 @@ DeviceTreeView::DeviceTreeView(QWidget *parent) : QTreeView(parent) {
|
||||
setItemDelegate(m_delegate);
|
||||
}
|
||||
|
||||
void DeviceTreeView::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
if (!(event->buttons() & Qt::LeftButton))
|
||||
return;
|
||||
|
||||
QDrag *drag = new QDrag(this);
|
||||
QMimeData *mimeData = new QMimeData;
|
||||
mimeData->setData("text/plain", selected_dbus_readables.join(",").toUtf8());
|
||||
drag->setMimeData(mimeData);
|
||||
|
||||
Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction);
|
||||
}
|
||||
|
||||
void DeviceTreeView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected){
|
||||
selected_dbus_readables.clear();
|
||||
|
||||
for(auto selection : selected){
|
||||
QModelIndexList selectedIndexes = selection.indexes();
|
||||
auto duplicated = std::unique(selectedIndexes.begin(), selectedIndexes.end(), [](QModelIndex &indexA, QModelIndex &indexB){
|
||||
return indexA.row() == indexB.row();
|
||||
});
|
||||
|
||||
selectedIndexes.erase(duplicated, selectedIndexes.end());
|
||||
|
||||
for(auto model_index : selectedIndexes){
|
||||
auto dynProxyV = model_index.data(DeviceModel::DynamicReadableProxyRole);
|
||||
|
||||
if (dynProxyV.isValid()) {
|
||||
auto proxy = qvariant_cast<DynamicReadableProxy *>(dynProxyV);
|
||||
|
||||
selected_dbus_readables.append(proxy->dbusPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceTreeView::suspendChildren(const QModelIndex &index) {
|
||||
// Somehow can be true when we collapse nodes from cache
|
||||
if (!Globals::g_deviceModel)
|
||||
|
@ -39,6 +39,8 @@ protected:
|
||||
// TODO: allow to start editing with the keyboard
|
||||
EditTriggers editTriggers() { return QAbstractItemView::AllEditTriggers; }
|
||||
void dataChanged(const QModelIndex &, const QModelIndex &, const QVector<int> &) override;
|
||||
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override;
|
||||
void mouseMoveEvent(QMouseEvent *event);
|
||||
private:
|
||||
void saveCollapsed(QAbstractItemModel *model);
|
||||
void restoreCollapsed(QAbstractItemModel *model);
|
||||
@ -52,4 +54,5 @@ private:
|
||||
QModelIndexList m_editSelection;
|
||||
QModelIndex m_editedIndex;
|
||||
DeviceModelDelegate *m_delegate;
|
||||
QStringList selected_dbus_readables;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user