mirror of
https://github.com/Lurkki14/tuxclocker.git
synced 2024-11-25 01:30:18 -06:00
qt: add basic assignable editing
This commit is contained in:
parent
138dc7d67f
commit
8b80c9c9a6
@ -4,13 +4,20 @@
|
||||
#include <QDBusMetaType>
|
||||
#include <QDBusReply>
|
||||
#include <QDebug>
|
||||
#include <QStandardItemModel>
|
||||
#include <QString>
|
||||
#include <QTreeView>
|
||||
#include <QVector>
|
||||
#include <Tree.hpp>
|
||||
|
||||
#include <DeviceModel.hpp>
|
||||
#include <DeviceModelDelegate.hpp>
|
||||
#include "MainWindow.hpp"
|
||||
|
||||
namespace TCDBus = TuxClocker::DBus;
|
||||
|
||||
using namespace TuxClocker;
|
||||
|
||||
Q_DECLARE_METATYPE(TCDBus::DeviceNode)
|
||||
Q_DECLARE_METATYPE(TCDBus::FlatTreeNode<TCDBus::DeviceNode>)
|
||||
|
||||
@ -24,10 +31,26 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
|
||||
|
||||
QDBusReply<QVector<TCDBus::FlatTreeNode<TCDBus::DeviceNode>>> reply =
|
||||
tuxclockerd.call("flatDeviceTree");
|
||||
|
||||
|
||||
// Convert to non-dbus flat tree
|
||||
FlatTree<TCDBus::DeviceNode> flatTree;
|
||||
if (reply.isValid()) {
|
||||
auto flatTree = reply.value();
|
||||
for (auto &f_node : flatTree)
|
||||
qDebug() << f_node.childIndices << f_node.value.interface;
|
||||
auto dbusFlatTree = reply.value();
|
||||
for (auto &f_node : dbusFlatTree) {
|
||||
//qDebug() << f_node.value.interface << f_node.value.path;
|
||||
FlatTreeNode<TCDBus::DeviceNode> node{
|
||||
f_node.value,
|
||||
f_node.childIndices.toStdVector()
|
||||
};
|
||||
flatTree.nodes.push_back(node);
|
||||
}
|
||||
}
|
||||
|
||||
auto root = flatTree.toTree(flatTree);
|
||||
|
||||
auto view = new QTreeView;
|
||||
view->setItemDelegate(new DeviceModelDelegate);
|
||||
auto model = new DeviceModel(root);
|
||||
view->setModel(model);
|
||||
setCentralWidget(view);
|
||||
}
|
||||
|
22
src/tuxclocker-qt/data/AssignableItem.hpp
Normal file
22
src/tuxclocker-qt/data/AssignableItem.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
//#include "DeviceModel.hpp"
|
||||
|
||||
#include <QObject>
|
||||
#include <QStandardItem>
|
||||
|
||||
// Class for forwarding changes in DeviceModel's assignables
|
||||
|
||||
// Forgive me for the sin of multiple inheritance
|
||||
class AssignableItem : public QObject, public QStandardItem {
|
||||
public:
|
||||
AssignableItem(QObject *parent = nullptr) : QObject(parent), QStandardItem() {
|
||||
}
|
||||
void setData(const QVariant &v, int role = Qt::UserRole + 1) {
|
||||
//if (role == DeviceModel::AssignableRole) ;
|
||||
}
|
||||
signals:
|
||||
void assignableDataChanged(QVariant value);
|
||||
private:
|
||||
Q_OBJECT
|
||||
};
|
22
src/tuxclocker-qt/data/AssignableItemData.hpp
Normal file
22
src/tuxclocker-qt/data/AssignableItemData.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <Device.hpp>
|
||||
#include <QVariant>
|
||||
|
||||
namespace TC = TuxClocker;
|
||||
|
||||
class AssignableItemData {
|
||||
public:
|
||||
AssignableItemData() {m_enabled = false;}
|
||||
AssignableItemData(TC::Device::AssignableInfo info) : m_info(info) {
|
||||
m_enabled = false;
|
||||
}
|
||||
TC::Device::AssignableInfo assignableInfo() {return m_info;}
|
||||
void setValue(QVariant v) {m_targetValue = v;}
|
||||
QVariant value() {return m_targetValue;}
|
||||
private:
|
||||
bool m_enabled;
|
||||
TC::Device::AssignableInfo m_info;
|
||||
QVariant m_targetValue;
|
||||
};
|
||||
Q_DECLARE_METATYPE(TC::Device::AssignableInfo)
|
78
src/tuxclocker-qt/data/DeviceModel.cpp
Normal file
78
src/tuxclocker-qt/data/DeviceModel.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
#include "DeviceModel.hpp"
|
||||
|
||||
Q_DECLARE_METATYPE(AssignableItemData)
|
||||
Q_DECLARE_METATYPE(TCDBus::Enumeration)
|
||||
Q_DECLARE_METATYPE(TCDBus::Range)
|
||||
|
||||
DeviceModel::DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent) :
|
||||
QStandardItemModel(parent) {
|
||||
qDBusRegisterMetaType<TCDBus::Enumeration>();
|
||||
qDBusRegisterMetaType<QVector<TCDBus::Enumeration>>();
|
||||
qDBusRegisterMetaType<TCDBus::Range>();
|
||||
/* Data storage:
|
||||
- Interface column should store assignable info for editors
|
||||
- Name colums should store the interface type for filtering
|
||||
- Parametrization/connection data, where? */
|
||||
|
||||
setColumnCount(2);
|
||||
|
||||
std::function<void(TC::TreeNode<TCDBus::DeviceNode> node, QStandardItem*)> traverse;
|
||||
traverse = [&traverse](auto node, auto item) {
|
||||
auto conn = QDBusConnection::systemBus();
|
||||
QDBusInterface nodeIface("org.tuxclocker", node.value().path, "org.tuxclocker.Node", conn);
|
||||
auto nodeName = nodeIface.property("name").toString();
|
||||
|
||||
QList<QStandardItem*> rowItems;
|
||||
auto nameItem = new QStandardItem;
|
||||
nameItem->setText(nodeName);
|
||||
rowItems.append(nameItem);
|
||||
|
||||
// Add assignable data if this is one
|
||||
if (node.value().interface == "org.tuxclocker.Assignable") {
|
||||
QDBusInterface ifaceNode("org.tuxclocker", node.value().path,
|
||||
"org.tuxclocker.Assignable", conn);
|
||||
// Should never fail
|
||||
auto a_info =
|
||||
qvariant_cast<QDBusVariant>(ifaceNode.property("assignableInfo"))
|
||||
.variant();
|
||||
/* TODO: bad hack: this code can only differentiate between
|
||||
arrays and structs */
|
||||
auto d_arg = qvariant_cast<QDBusArgument>(a_info);
|
||||
switch (d_arg.currentType()) {
|
||||
case QDBusArgument::StructureType: {
|
||||
auto ifaceItem = new QStandardItem;
|
||||
TCDBus::Range r;
|
||||
d_arg >> r;
|
||||
QVariant v;
|
||||
AssignableItemData data(r.toAssignableInfo());
|
||||
v.setValue(data);
|
||||
ifaceItem->setData(v, Role::AssignableRole);
|
||||
ifaceItem->setText(r.min.variant().toString());
|
||||
rowItems.append(ifaceItem);
|
||||
break;
|
||||
}
|
||||
case QDBusArgument::ArrayType: {
|
||||
auto ifaceItem = new QStandardItem;
|
||||
QVector<TCDBus::Enumeration> e;
|
||||
d_arg >> e;
|
||||
QVariant v;
|
||||
v.setValue(e);
|
||||
ifaceItem->setData(v, Role::AssignableRole);
|
||||
ifaceItem->setText(e.first().name);
|
||||
rowItems.append(ifaceItem);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
item->appendRow(rowItems);
|
||||
|
||||
for (auto c_node : node.children())
|
||||
traverse(c_node, nameItem);
|
||||
};
|
||||
auto rootItem = invisibleRootItem();
|
||||
|
||||
for (auto &node : root.children())
|
||||
traverse(node, rootItem);
|
||||
}
|
35
src/tuxclocker-qt/data/DeviceModel.hpp
Normal file
35
src/tuxclocker-qt/data/DeviceModel.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
//#include "AssignableItem.hpp"
|
||||
#include "AssignableItemData.hpp"
|
||||
|
||||
#include <DBusTypes.hpp>
|
||||
#include <Device.hpp>
|
||||
#include <patterns.hpp>
|
||||
#include <Tree.hpp>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusInterface>
|
||||
#include <QStandardItemModel>
|
||||
|
||||
namespace p = mpark::patterns;
|
||||
namespace TC = TuxClocker;
|
||||
namespace TCDBus = TuxClocker::DBus;
|
||||
|
||||
|
||||
class DeviceModel : public QStandardItemModel {
|
||||
public:
|
||||
DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent = nullptr);
|
||||
enum ColumnType {
|
||||
Name = 0, // Node name
|
||||
Interface = 1 // Column for presenting interfaces
|
||||
};
|
||||
|
||||
enum Role {
|
||||
AssignableRole = Qt::UserRole, // Holds the data about the assignable
|
||||
ConnectionRole // Data about the connection
|
||||
};
|
||||
|
||||
enum class FilterFlag {
|
||||
|
||||
};
|
||||
};
|
58
src/tuxclocker-qt/data/DeviceModelDelegate.cpp
Normal file
58
src/tuxclocker-qt/data/DeviceModelDelegate.cpp
Normal file
@ -0,0 +1,58 @@
|
||||
#include "DeviceModelDelegate.hpp"
|
||||
|
||||
#include "DeviceModel.hpp"
|
||||
#include <DoubleRangeEditor.hpp>
|
||||
#include <IntRangeEditor.hpp>
|
||||
#include <patterns.hpp>
|
||||
|
||||
using namespace TuxClocker::Device;
|
||||
using namespace mpark::patterns;
|
||||
|
||||
Q_DECLARE_METATYPE(AssignableItemData)
|
||||
|
||||
DeviceModelDelegate::DeviceModelDelegate(QObject* parent) :
|
||||
QStyledItemDelegate(parent) {}
|
||||
|
||||
QWidget *DeviceModelDelegate::createEditor(QWidget *parent,
|
||||
const QStyleOptionViewItem&, const QModelIndex &index) const {
|
||||
auto v = index.data(DeviceModel::AssignableRole);
|
||||
QWidget *editor = nullptr;
|
||||
if (v.canConvert<AssignableItemData>()) {
|
||||
match(v.value<AssignableItemData>().assignableInfo())
|
||||
(pattern(as<RangeInfo>(arg)) = [&](auto r_info) {
|
||||
match(r_info)
|
||||
(pattern(as<Range<int>>(arg)) = [&](auto ir) {
|
||||
editor = new IntRangeEditor(ir, parent);
|
||||
},
|
||||
pattern(as<Range<double>>(arg)) = [&](auto dr) {
|
||||
editor = new DoubleRangeEditor(dr, parent);
|
||||
});
|
||||
});
|
||||
}
|
||||
return editor;
|
||||
}
|
||||
|
||||
void DeviceModelDelegate::setEditorData(QWidget *editor,
|
||||
const QModelIndex &index) const {
|
||||
auto v = index.data(DeviceModel::AssignableRole);
|
||||
if (v.canConvert<AssignableItemData>()) {
|
||||
auto data = v.value<AssignableItemData>().value();
|
||||
auto a_editor = static_cast<AbstractAssignableEditor*>(editor);
|
||||
a_editor->setAssignableData(data);
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceModelDelegate::setModelData(QWidget *editor,
|
||||
QAbstractItemModel *model, const QModelIndex &index) const {
|
||||
auto v = index.data(DeviceModel::AssignableRole);
|
||||
if (v.canConvert<AssignableItemData>()) {
|
||||
auto a_editor = static_cast<AbstractAssignableEditor*>(editor);
|
||||
auto data = index.data(DeviceModel::AssignableRole)
|
||||
.value<AssignableItemData>();
|
||||
data.setValue(a_editor->assignableData());
|
||||
QVariant v;
|
||||
v.setValue(data);
|
||||
model->setData(index, a_editor->displayData(), Qt::DisplayRole);
|
||||
model->setData(index, v, DeviceModel::AssignableRole);
|
||||
}
|
||||
}
|
15
src/tuxclocker-qt/data/DeviceModelDelegate.hpp
Normal file
15
src/tuxclocker-qt/data/DeviceModelDelegate.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
class DeviceModelDelegate : public QStyledItemDelegate {
|
||||
public:
|
||||
DeviceModelDelegate(QObject *parent = nullptr);
|
||||
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const;
|
||||
//void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
|
||||
// const QModelIndex &index) const;
|
||||
void setEditorData(QWidget *editor, const QModelIndex &index) const;
|
||||
void setModelData(QWidget *editor, QAbstractItemModel *model,
|
||||
const QModelIndex &index) const;
|
||||
};
|
@ -2,20 +2,11 @@
|
||||
|
||||
#include "MainWindow.hpp"
|
||||
|
||||
#include <QDBusConnection>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
QApplication app(argc, argv);
|
||||
|
||||
MainWindow mw;
|
||||
mw.show();
|
||||
|
||||
QObject o;
|
||||
|
||||
auto conn = QDBusConnection::sessionBus();
|
||||
conn.registerObject("/", &o);
|
||||
|
||||
conn.registerService("org.tuxclockerqt");
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
@ -1,15 +1,22 @@
|
||||
qt5 = import('qt5')
|
||||
qt5_dep = dependency('qt5', modules: ['DBus', 'Charts', 'Widgets'])
|
||||
|
||||
moc_files = qt5.preprocess(moc_headers: ['MainWindow.hpp'],
|
||||
moc_files = qt5.preprocess(moc_headers: ['MainWindow.hpp',
|
||||
'data/AssignableItem.hpp'],
|
||||
dependencies: qt5_dep)
|
||||
|
||||
sources = ['main.cpp',
|
||||
'data/DeviceModel.cpp',
|
||||
'data/DeviceModelDelegate.cpp',
|
||||
'MainWindow.cpp']
|
||||
|
||||
local_incdir = include_directories(['data',
|
||||
'widgets',
|
||||
'../include/deps/patterns/include/mpark'])
|
||||
|
||||
executable('tuxclocker-qt',
|
||||
moc_files,
|
||||
sources,
|
||||
dependencies: qt5_dep,
|
||||
include_directories: [incdir],
|
||||
include_directories: [incdir, local_incdir],
|
||||
install: true)
|
||||
|
12
src/tuxclocker-qt/widgets/AbstractAssignableEditor.hpp
Normal file
12
src/tuxclocker-qt/widgets/AbstractAssignableEditor.hpp
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class AbstractAssignableEditor : public QWidget {
|
||||
public:
|
||||
AbstractAssignableEditor(QWidget *parent = nullptr) : QWidget(parent) {}
|
||||
virtual ~AbstractAssignableEditor() {}
|
||||
virtual QVariant assignableData() = 0;
|
||||
virtual QString displayData() = 0;
|
||||
virtual void setAssignableData(QVariant data) = 0;
|
||||
};
|
65
src/tuxclocker-qt/widgets/DoubleRangeEditor.hpp
Normal file
65
src/tuxclocker-qt/widgets/DoubleRangeEditor.hpp
Normal file
@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include "AbstractAssignableEditor.hpp"
|
||||
|
||||
#include <Device.hpp>
|
||||
#include <tgmath.h>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QSlider>
|
||||
|
||||
namespace TC = TuxClocker;
|
||||
|
||||
class DoubleRangeEditor : public AbstractAssignableEditor {
|
||||
public:
|
||||
DoubleRangeEditor(QWidget *parent = nullptr)
|
||||
: AbstractAssignableEditor(parent) {
|
||||
setAutoFillBackground(true);
|
||||
m_layout = new QHBoxLayout(this);
|
||||
m_spinBox = new QDoubleSpinBox;
|
||||
m_slider = new QSlider(Qt::Horizontal);
|
||||
|
||||
connect(m_slider, &QSlider::rangeChanged, [this](int min, int max) {
|
||||
m_spinBox->setRange(toDouble(min), toDouble(max));
|
||||
});
|
||||
connect(m_spinBox, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
|
||||
[this](double val) {
|
||||
m_slider->setValue(toInt(val));
|
||||
});
|
||||
connect(m_slider, &QSlider::valueChanged, [this](int val) {
|
||||
m_spinBox->setValue(toDouble(val));
|
||||
});
|
||||
|
||||
m_layout->setMargin(0);
|
||||
m_layout->addWidget(m_slider);
|
||||
m_layout->addWidget(m_spinBox);
|
||||
setLayout(m_layout);
|
||||
}
|
||||
DoubleRangeEditor(TC::Device::Range<double> range, QWidget *parent = nullptr)
|
||||
: DoubleRangeEditor(parent) {
|
||||
setRange(range);
|
||||
}
|
||||
virtual QVariant assignableData() override {return m_spinBox->value();}
|
||||
virtual QString displayData() override {
|
||||
return QString::number(m_spinBox->value());
|
||||
}
|
||||
virtual void setAssignableData(QVariant data) override {
|
||||
m_spinBox->setValue(data.toDouble());
|
||||
}
|
||||
void setRange(TC::Device::Range<double> range) {
|
||||
m_slider->setRange(toInt(range.min), toInt(range.max));
|
||||
}
|
||||
private:
|
||||
// How many digits are displayed after the dot
|
||||
constexpr int doubleDecimals() {return 2;}
|
||||
double toDouble(int i) {
|
||||
return static_cast<double>(i) / pow(10, doubleDecimals());
|
||||
}
|
||||
int toInt(double d) {
|
||||
return static_cast<int>(d * pow(10, doubleDecimals()));
|
||||
}
|
||||
|
||||
QDoubleSpinBox *m_spinBox;
|
||||
QHBoxLayout *m_layout;
|
||||
QSlider *m_slider;
|
||||
};
|
50
src/tuxclocker-qt/widgets/IntRangeEditor.hpp
Normal file
50
src/tuxclocker-qt/widgets/IntRangeEditor.hpp
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "AbstractAssignableEditor.hpp"
|
||||
|
||||
#include <Device.hpp>
|
||||
#include <QSlider>
|
||||
#include <QSpinBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
namespace TC = TuxClocker;
|
||||
|
||||
class IntRangeEditor : public AbstractAssignableEditor {
|
||||
public:
|
||||
IntRangeEditor(QWidget *parent = nullptr) : AbstractAssignableEditor(parent) {
|
||||
setAutoFillBackground(true);
|
||||
|
||||
m_layout = new QHBoxLayout(this);
|
||||
m_layout->setMargin(0);
|
||||
m_slider = new QSlider(Qt::Horizontal);
|
||||
m_spinBox = new QSpinBox;
|
||||
|
||||
m_layout->addWidget(m_slider);
|
||||
m_layout->addWidget(m_spinBox);
|
||||
|
||||
setLayout(m_layout);
|
||||
|
||||
connect(m_slider, &QSlider::rangeChanged, m_spinBox, &QSpinBox::setRange);
|
||||
connect(m_slider, &QSlider::valueChanged, m_spinBox, &QSpinBox::setValue);
|
||||
connect(m_spinBox, QOverload<int>::of(&QSpinBox::valueChanged), m_slider, &QSlider::setValue);
|
||||
}
|
||||
IntRangeEditor(TC::Device::Range<int> range, QWidget *parent = nullptr)
|
||||
: IntRangeEditor(parent) {
|
||||
setRange(range);
|
||||
}
|
||||
|
||||
virtual QVariant assignableData() override {return m_slider->value();}
|
||||
virtual QString displayData() override {
|
||||
return QString::number(m_slider->value());
|
||||
}
|
||||
virtual void setAssignableData(QVariant data) override {m_slider->setValue(data.toInt());}
|
||||
void setRange(TC::Device::Range<int> range) {
|
||||
m_slider->setRange(range.min, range.max);
|
||||
}
|
||||
int value() {return m_slider->value();}
|
||||
private:
|
||||
QHBoxLayout *m_layout;
|
||||
QSlider *m_slider;
|
||||
QSpinBox *m_spinBox;
|
||||
};
|
5
src/tuxclocker-qt/widgets/MainBrowser.hpp
Normal file
5
src/tuxclocker-qt/widgets/MainBrowser.hpp
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
// Primary widget for viewing the tuxclocker device tree.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user