mirror of
https://github.com/Lurkki14/tuxclocker.git
synced 2025-02-25 18:55:24 -06:00
qt: add parametrization basics
This commit is contained in:
parent
b29495629a
commit
b0c39bfaeb
6
src/tuxclocker-qt/data/AbstractAssignableConnection.hpp
Normal file
6
src/tuxclocker-qt/data/AbstractAssignableConnection.hpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
class AbstractAssignableConnection {
|
||||||
|
public:
|
||||||
|
|
||||||
|
};
|
32
src/tuxclocker-qt/data/AssignableConnection.hpp
Normal file
32
src/tuxclocker-qt/data/AssignableConnection.hpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/signals2.hpp>
|
||||||
|
#include <functional>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
// Interface for the function for an assignable
|
||||||
|
class AssignableConnection : public QObject {
|
||||||
|
public:
|
||||||
|
AssignableConnection(QObject *parent = nullptr) : QObject(parent) {}
|
||||||
|
/*AssignableConnection(const AssignableConnection &other) {
|
||||||
|
targetValueChanged.connect(other.targetValueChanged);
|
||||||
|
}*/
|
||||||
|
virtual ~AssignableConnection() {}
|
||||||
|
// Returns the data as QVariant for saving to settings
|
||||||
|
virtual QVariant connectionData() = 0;
|
||||||
|
virtual void start() = 0;
|
||||||
|
virtual void stop() = 0;
|
||||||
|
/* Value and text representation of desired value,
|
||||||
|
since converting QDBusVariant to QString isn't trivial
|
||||||
|
for complex types. */
|
||||||
|
/*boost::signals2::signal<void(QVariant, QString)> targetValueChanged;
|
||||||
|
boost::signals2::signal<void()> started;
|
||||||
|
boost::signals2::signal<void()> stopped;*/
|
||||||
|
signals:
|
||||||
|
void targetValueChanged(QVariant, QString);
|
||||||
|
void started();
|
||||||
|
void stopped();
|
||||||
|
private:
|
||||||
|
Q_OBJECT
|
||||||
|
};
|
@ -1,5 +1,6 @@
|
|||||||
#include "AssignableProxy.hpp"
|
#include "AssignableProxy.hpp"
|
||||||
|
|
||||||
|
|
||||||
#include <DBusTypes.hpp>
|
#include <DBusTypes.hpp>
|
||||||
#include <QDBusReply>
|
#include <QDBusReply>
|
||||||
#include <QDBusMessage>
|
#include <QDBusMessage>
|
||||||
@ -16,7 +17,20 @@ AssignableProxy::AssignableProxy(QString path, QDBusConnection conn,
|
|||||||
m_iface = new QDBusInterface("org.tuxclocker",
|
m_iface = new QDBusInterface("org.tuxclocker",
|
||||||
path, "org.tuxclocker.Assignable", conn, this);
|
path, "org.tuxclocker.Assignable", conn, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<AssignmentError> AssignableProxy::doApply(const QVariant &v) {
|
||||||
|
//qDebug() << v;
|
||||||
|
QDBusReply<TCD::Result<int>> reply = m_iface->call("assign", v);
|
||||||
|
if (reply.isValid()) {
|
||||||
|
TCD::Result<AssignmentError> ar{reply.value().error,
|
||||||
|
static_cast<AssignmentError>(reply.value().value)};
|
||||||
|
//qDebug("Success!");
|
||||||
|
return ar.toOptional();
|
||||||
|
}
|
||||||
|
// TODO: indicate dbus error
|
||||||
|
return AssignmentError::UnknownError;
|
||||||
|
}
|
||||||
|
|
||||||
void AssignableProxy::apply() {
|
void AssignableProxy::apply() {
|
||||||
// A value hasn't been set yet
|
// A value hasn't been set yet
|
||||||
if (m_value.isNull())
|
if (m_value.isNull())
|
||||||
@ -26,18 +40,21 @@ void AssignableProxy::apply() {
|
|||||||
QDBusVariant dv(m_value);
|
QDBusVariant dv(m_value);
|
||||||
QVariant v;
|
QVariant v;
|
||||||
v.setValue(dv);
|
v.setValue(dv);
|
||||||
QDBusReply<TCD::Result<int>> reply = m_iface->call("assign", v);
|
emit applied(doApply(v));
|
||||||
if (reply.isValid()) {
|
|
||||||
qDebug() << reply.value().error << reply.value().value;
|
|
||||||
|
|
||||||
TCD::Result<AssignmentError> ar{reply.value().error,
|
|
||||||
static_cast<AssignmentError>(reply.value().value)};
|
|
||||||
qDebug("Success!");
|
|
||||||
emit applied(ar.toOptional());
|
|
||||||
}
|
|
||||||
// TODO: Maybe indicate that calling dbus errored out
|
|
||||||
else
|
|
||||||
emit applied(AssignmentError::UnknownError);
|
|
||||||
// Indicate that there's no pending value to applied by making value invalid
|
// Indicate that there's no pending value to applied by making value invalid
|
||||||
m_value = QVariant();
|
m_value = QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AssignableProxy::startConnection(std::shared_ptr<AssignableConnection> conn) {
|
||||||
|
m_assignableConnection = conn;
|
||||||
|
connect(conn.get(), &AssignableConnection::targetValueChanged, [this](auto targetValue, auto text) {
|
||||||
|
auto err = doApply(targetValue);
|
||||||
|
if (err.has_value())
|
||||||
|
emit connectionValueChanged(err.value(), text);
|
||||||
|
else
|
||||||
|
emit connectionValueChanged(targetValue, text);
|
||||||
|
});
|
||||||
|
// Emit started signal in case a connection emits a new value right away
|
||||||
|
emit connectionStarted();
|
||||||
|
m_assignableConnection->start();
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
// Acts as a buffer for unapplied assignables and applies them asynchronously
|
// Acts as a buffer for unapplied assignables and applies them asynchronously
|
||||||
|
|
||||||
|
#include <AssignableConnection.hpp>
|
||||||
#include <Device.hpp>
|
#include <Device.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
@ -17,12 +18,23 @@ public:
|
|||||||
AssignableProxy(QString path, QDBusConnection conn,
|
AssignableProxy(QString path, QDBusConnection conn,
|
||||||
QObject *parent = nullptr);
|
QObject *parent = nullptr);
|
||||||
void apply();
|
void apply();
|
||||||
|
void startConnection(std::shared_ptr<AssignableConnection> conn);
|
||||||
|
// Stop connection and clear current connection
|
||||||
|
void stopConnection();
|
||||||
void setValue(QVariant v) {m_value = v;}
|
void setValue(QVariant v) {m_value = v;}
|
||||||
signals:
|
signals:
|
||||||
void applied(std::optional<TC::Device::AssignmentError>);
|
void applied(std::optional<TC::Device::AssignmentError>);
|
||||||
|
void connectionValueChanged(std::variant<QVariant, TC::Device::AssignmentError>,
|
||||||
|
QString text);
|
||||||
|
void connectionStarted();
|
||||||
|
void connectionStopped();
|
||||||
private:
|
private:
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
QVariant m_value;
|
QVariant m_value;
|
||||||
QDBusInterface *m_iface;
|
QDBusInterface *m_iface;
|
||||||
|
// This is a bit of a peril but not sure if we can store interfaces any better...
|
||||||
|
std::shared_ptr<AssignableConnection> m_assignableConnection;
|
||||||
|
|
||||||
|
std::optional<TC::Device::AssignmentError> doApply(const QVariant &v);
|
||||||
};
|
};
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include "AssignableProxy.hpp"
|
#include "AssignableProxy.hpp"
|
||||||
#include "DynamicReadableProxy.hpp"
|
#include "DynamicReadableProxy.hpp"
|
||||||
|
#include "qnamespace.h"
|
||||||
|
#include "qstandarditemmodel.h"
|
||||||
#include <fplus/fplus.hpp>
|
#include <fplus/fplus.hpp>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDBusReply>
|
#include <QDBusReply>
|
||||||
@ -16,6 +18,8 @@ using namespace mpark::patterns;
|
|||||||
using namespace TuxClocker::Device;
|
using namespace TuxClocker::Device;
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(AssignableItemData)
|
Q_DECLARE_METATYPE(AssignableItemData)
|
||||||
|
Q_DECLARE_METATYPE(AssignableProxy*)
|
||||||
|
Q_DECLARE_METATYPE(DynamicReadableProxy*)
|
||||||
Q_DECLARE_METATYPE(TCDBus::Enumeration)
|
Q_DECLARE_METATYPE(TCDBus::Enumeration)
|
||||||
Q_DECLARE_METATYPE(TCDBus::Range)
|
Q_DECLARE_METATYPE(TCDBus::Range)
|
||||||
Q_DECLARE_METATYPE(EnumerationVec)
|
Q_DECLARE_METATYPE(EnumerationVec)
|
||||||
@ -50,13 +54,8 @@ DeviceModel::DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent)
|
|||||||
if_let(pattern(some(arg)) = setupAssignable(node, conn))
|
if_let(pattern(some(arg)) = setupAssignable(node, conn))
|
||||||
= [&](auto item) {
|
= [&](auto item) {
|
||||||
nameItem->setData(Assignable, InterfaceTypeRole);
|
nameItem->setData(Assignable, InterfaceTypeRole);
|
||||||
|
|
||||||
//auto style = QApplication::style();
|
|
||||||
//auto icon = style->standardIcon(QStyle::SP_ComputerIcon);
|
|
||||||
auto icon = assignableIcon();
|
auto icon = assignableIcon();
|
||||||
//QIcon icon("/home/jussi/Downloads/wrench.png");
|
|
||||||
nameItem->setData(icon, Qt::DecorationRole);
|
nameItem->setData(icon, Qt::DecorationRole);
|
||||||
|
|
||||||
rowItems.append(item);
|
rowItems.append(item);
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -69,6 +68,7 @@ DeviceModel::DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent)
|
|||||||
nameItem->setData(DeviceModel::DynamicReadable,
|
nameItem->setData(DeviceModel::DynamicReadable,
|
||||||
InterfaceTypeRole);
|
InterfaceTypeRole);
|
||||||
rowItems.append(item);
|
rowItems.append(item);
|
||||||
|
//qDebug() << item->data(DynamicReadableProxyRole);
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
pattern("org.tuxclocker.StaticReadable") = [=, &rowItems] {
|
pattern("org.tuxclocker.StaticReadable") = [=, &rowItems] {
|
||||||
@ -100,10 +100,35 @@ EnumerationVec toEnumVec(QVector<TCDBus::Enumeration> enums) {
|
|||||||
}, enums.toStdVector());
|
}, enums.toStdVector());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<const AssignableProxy*>
|
||||||
|
DeviceModel::assignableProxyFromItem(QStandardItem *item) {
|
||||||
|
return (m_assignableProxyHash.contains(item)) ?
|
||||||
|
std::optional(m_assignableProxyHash.value(item)) :
|
||||||
|
std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
QStandardItem *DeviceModel::createAssignable(TC::TreeNode<TCDBus::DeviceNode> node,
|
QStandardItem *DeviceModel::createAssignable(TC::TreeNode<TCDBus::DeviceNode> node,
|
||||||
QDBusConnection conn, AssignableItemData itemData) {
|
QDBusConnection conn, AssignableItemData itemData) {
|
||||||
auto ifaceItem = new AssignableItem(this);
|
auto ifaceItem = new AssignableItem(this);
|
||||||
auto proxy = new AssignableProxy(node.value().path, conn, this);
|
auto proxy = new AssignableProxy(node.value().path, conn, this);
|
||||||
|
|
||||||
|
connect(proxy, &AssignableProxy::connectionValueChanged, [=] (auto result, auto text) {
|
||||||
|
p::match(result) (
|
||||||
|
pattern(as<QVariant>(arg)) = [=](auto v) {
|
||||||
|
QVariant data;
|
||||||
|
data.setValue(connectionColor());
|
||||||
|
ifaceItem->setData(data, Qt::BackgroundRole);
|
||||||
|
ifaceItem->setText(text);
|
||||||
|
//qDebug() << text;
|
||||||
|
},
|
||||||
|
pattern(_) = []{}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
QVariant pv;
|
||||||
|
pv.setValue(proxy);
|
||||||
|
ifaceItem->setData(pv, AssignableProxyRole);
|
||||||
QVariant v;
|
QVariant v;
|
||||||
v.setValue(itemData);
|
v.setValue(itemData);
|
||||||
ifaceItem->setData(v, AssignableRole);
|
ifaceItem->setData(v, AssignableRole);
|
||||||
@ -159,6 +184,21 @@ QStandardItem *DeviceModel::createAssignable(TC::TreeNode<TCDBus::DeviceNode> no
|
|||||||
return ifaceItem;
|
return ifaceItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariant DeviceModel::data(const QModelIndex &index, int role) const {
|
||||||
|
if (index.row() != DeviceModel::NameColumn && role == DeviceModel::NodeNameRole) {
|
||||||
|
// Get name from adjacent column
|
||||||
|
auto nameIndex =
|
||||||
|
this->index(index.row(), DeviceModel::NameColumn, index.parent());
|
||||||
|
return nameIndex.data(Qt::DisplayRole);
|
||||||
|
}
|
||||||
|
if (index.column() != InterfaceColumn && role == DynamicReadableProxyRole) {
|
||||||
|
auto idx =
|
||||||
|
this->index(index.row(), DeviceModel::InterfaceColumn, index.parent());
|
||||||
|
return idx.data(DynamicReadableProxyRole);
|
||||||
|
}
|
||||||
|
return QStandardItemModel::data(index, role);
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<QStandardItem*> DeviceModel::setupAssignable(
|
std::optional<QStandardItem*> DeviceModel::setupAssignable(
|
||||||
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn) {
|
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn) {
|
||||||
QDBusInterface ifaceNode("org.tuxclocker", node.value().path,
|
QDBusInterface ifaceNode("org.tuxclocker", node.value().path,
|
||||||
@ -197,13 +237,17 @@ void updateReadItemText(QStandardItem *item, T value,
|
|||||||
QString("%1 %2").arg(value).arg(unit.value()) :
|
QString("%1 %2").arg(value).arg(unit.value()) :
|
||||||
QString("%1").arg(value);
|
QString("%1").arg(value);
|
||||||
item->setText(text);
|
item->setText(text);
|
||||||
|
//qDebug() << item->data(DeviceModel::DynamicReadableProxyRole);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<QStandardItem*> DeviceModel::setupDynReadable(
|
std::optional<QStandardItem*> DeviceModel::setupDynReadable(
|
||||||
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn) {
|
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn) {
|
||||||
auto item = new QStandardItem;
|
auto item = new QStandardItem;
|
||||||
auto proxy = new DynamicReadableProxy(node.value().path, conn, this);
|
auto proxy = new DynamicReadableProxy(node.value().path, conn, this);
|
||||||
auto unit = proxy->unit();
|
QVariant v;
|
||||||
|
v.setValue(proxy);
|
||||||
|
item->setData(v, DynamicReadableProxyRole);
|
||||||
|
auto unit = proxy->unit();
|
||||||
|
|
||||||
connect(proxy, &DynamicReadableProxy::valueChanged, [=](ReadResult res) {
|
connect(proxy, &DynamicReadableProxy::valueChanged, [=](ReadResult res) {
|
||||||
p::match(res)(
|
p::match(res)(
|
||||||
|
@ -2,23 +2,26 @@
|
|||||||
|
|
||||||
#include "AssignableItem.hpp"
|
#include "AssignableItem.hpp"
|
||||||
#include "AssignableItemData.hpp"
|
#include "AssignableItemData.hpp"
|
||||||
|
#include "DynamicReadableProxy.hpp"
|
||||||
|
|
||||||
#include <DBusTypes.hpp>
|
#include <DBusTypes.hpp>
|
||||||
#include <Device.hpp>
|
#include <Device.hpp>
|
||||||
#include <patterns.hpp>
|
#include <patterns.hpp>
|
||||||
#include <Tree.hpp>
|
|
||||||
#include <QDBusConnection>
|
#include <QDBusConnection>
|
||||||
#include <QDBusInterface>
|
#include <QDBusInterface>
|
||||||
#include <QFlags>
|
#include <QFlags>
|
||||||
|
#include <QHash>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QStandardItemModel>
|
#include <QStandardItemModel>
|
||||||
#include <QPalette>
|
#include <QPalette>
|
||||||
|
#include <Tree.hpp>
|
||||||
|
|
||||||
namespace TC = TuxClocker;
|
namespace TC = TuxClocker;
|
||||||
namespace TCDBus = TuxClocker::DBus;
|
namespace TCDBus = TuxClocker::DBus;
|
||||||
|
|
||||||
// Why the fuck do I have to forward declare this?
|
// Why the fuck do I have to forward declare this?
|
||||||
class AssignableItem;
|
class AssignableItem;
|
||||||
|
class AssignableProxy;
|
||||||
|
|
||||||
class DeviceModel : public QStandardItemModel {
|
class DeviceModel : public QStandardItemModel {
|
||||||
public:
|
public:
|
||||||
@ -30,8 +33,11 @@ public:
|
|||||||
|
|
||||||
enum Role {
|
enum Role {
|
||||||
AssignableRole = Qt::UserRole, // Holds the data about the assignable
|
AssignableRole = Qt::UserRole, // Holds the data about the assignable
|
||||||
|
AssignableProxyRole,
|
||||||
ConnectionRole, // Data about the connection
|
ConnectionRole, // Data about the connection
|
||||||
InterfaceTypeRole // InterfaceType
|
DynamicReadableProxyRole,
|
||||||
|
InterfaceTypeRole, // InterfaceType
|
||||||
|
NodeNameRole //
|
||||||
};
|
};
|
||||||
|
|
||||||
enum InterfaceFlag {
|
enum InterfaceFlag {
|
||||||
@ -41,14 +47,15 @@ public:
|
|||||||
AllInterfaces = (Assignable | DynamicReadable | StaticReadable)
|
AllInterfaces = (Assignable | DynamicReadable | StaticReadable)
|
||||||
};
|
};
|
||||||
typedef QFlags<InterfaceFlag> InterfaceFlags;
|
typedef QFlags<InterfaceFlag> InterfaceFlags;
|
||||||
|
|
||||||
enum class FilterFlag {
|
|
||||||
|
|
||||||
};
|
|
||||||
// For decoupling AssignableItems created in the model
|
// For decoupling AssignableItems created in the model
|
||||||
void applyChanges() {emit changesApplied();}
|
void applyChanges() {emit changesApplied();}
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
static QIcon assignableIcon() {return QIcon::fromTheme("edit-entry");}
|
static QIcon assignableIcon() {return QIcon::fromTheme("edit-entry");}
|
||||||
|
// Get AssignableProxy of an item
|
||||||
|
std::optional<const AssignableProxy*> assignableProxyFromItem(QStandardItem *item);
|
||||||
|
// AssignableProxies are associated with both row items
|
||||||
|
//QVariant data(QModelIndex&);
|
||||||
static QIcon dynamicReadableIcon() {return QIcon(":/ruler.svg");}
|
static QIcon dynamicReadableIcon() {return QIcon(":/ruler.svg");}
|
||||||
static QIcon staticReadableIcon() {return QIcon::fromTheme("help-about");}
|
static QIcon staticReadableIcon() {return QIcon::fromTheme("help-about");}
|
||||||
signals:
|
signals:
|
||||||
@ -56,6 +63,8 @@ signals:
|
|||||||
private:
|
private:
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
QHash<QStandardItem*, AssignableProxy*> m_assignableProxyHash;
|
||||||
|
|
||||||
// Separate handling interfaces since otherwise we run out of columns
|
// Separate handling interfaces since otherwise we run out of columns
|
||||||
QStandardItem *createAssignable(TC::TreeNode<TCDBus::DeviceNode> node,
|
QStandardItem *createAssignable(TC::TreeNode<TCDBus::DeviceNode> node,
|
||||||
QDBusConnection conn, AssignableItemData data);
|
QDBusConnection conn, AssignableItemData data);
|
||||||
@ -68,6 +77,7 @@ private:
|
|||||||
constexpr int fadeOutTime() {return 5000;} // milliseconds
|
constexpr int fadeOutTime() {return 5000;} // milliseconds
|
||||||
constexpr int transparency() {return 120;} // 0-255
|
constexpr int transparency() {return 120;} // 0-255
|
||||||
// Colors for items
|
// Colors for items
|
||||||
|
QColor connectionColor() {return QColor(0, 0, 255, transparency());} // blue
|
||||||
QColor errorColor() {return QColor(255, 0, 0, transparency());} // red
|
QColor errorColor() {return QColor(255, 0, 0, transparency());} // red
|
||||||
QColor unappliedColor() {return QColor(255, 255, 0, transparency());} // yellow
|
QColor unappliedColor() {return QColor(255, 255, 0, transparency());} // yellow
|
||||||
QColor successColor() {return QColor(0, 255, 0, transparency());} // green
|
QColor successColor() {return QColor(0, 255, 0, transparency());} // green
|
||||||
|
@ -5,7 +5,8 @@
|
|||||||
Q_DECLARE_METATYPE(DeviceModel::InterfaceFlag)
|
Q_DECLARE_METATYPE(DeviceModel::InterfaceFlag)
|
||||||
|
|
||||||
DeviceProxyModel::DeviceProxyModel(DeviceModel &model, QObject *parent) :
|
DeviceProxyModel::DeviceProxyModel(DeviceModel &model, QObject *parent) :
|
||||||
QSortFilterProxyModel(parent), m_showIcons(true), m_showValueColumn(true) {
|
QSortFilterProxyModel(parent), m_disableFiltered(false),
|
||||||
|
m_showIcons(true), m_showValueColumn(true) {
|
||||||
setSourceModel(&model);
|
setSourceModel(&model);
|
||||||
m_flags = DeviceModel::AllInterfaces;
|
m_flags = DeviceModel::AllInterfaces;
|
||||||
}
|
}
|
||||||
@ -37,6 +38,22 @@ bool DeviceProxyModel::filterAcceptsRow(int sourceRow,
|
|||||||
return !shouldHide;
|
return !shouldHide;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Qt::ItemFlags DeviceProxyModel::flags(const QModelIndex &index) const {
|
||||||
|
if (!m_disableFiltered)
|
||||||
|
return QSortFilterProxyModel::flags(index);
|
||||||
|
|
||||||
|
auto iface =
|
||||||
|
QSortFilterProxyModel::data(index, DeviceModel::InterfaceTypeRole);
|
||||||
|
auto removedFlags = Qt::ItemIsSelectable;
|
||||||
|
auto itemFlags = QSortFilterProxyModel::flags(index);
|
||||||
|
if (!iface.isValid())
|
||||||
|
// Remove selectable flag
|
||||||
|
return itemFlags &(~removedFlags);
|
||||||
|
auto flags = iface.value<DeviceModel::InterfaceFlag>();
|
||||||
|
return (flags & m_flags) ? itemFlags :
|
||||||
|
itemFlags &(~removedFlags);
|
||||||
|
}
|
||||||
|
|
||||||
bool DeviceProxyModel::lessThan(const QModelIndex &left,
|
bool DeviceProxyModel::lessThan(const QModelIndex &left,
|
||||||
const QModelIndex &right) const {
|
const QModelIndex &right) const {
|
||||||
// TODO: doesn't work as expected if sorted from interface column
|
// TODO: doesn't work as expected if sorted from interface column
|
||||||
|
@ -9,6 +9,8 @@ class DeviceProxyModel : public QSortFilterProxyModel {
|
|||||||
public:
|
public:
|
||||||
DeviceProxyModel(DeviceModel &model, QObject *parent = nullptr);
|
DeviceProxyModel(DeviceModel &model, QObject *parent = nullptr);
|
||||||
DeviceModel::InterfaceFlags filterFlags() {return m_flags;}
|
DeviceModel::InterfaceFlags filterFlags() {return m_flags;}
|
||||||
|
// Disable items that don't contain the interface in m_flags
|
||||||
|
void setDisableFiltered(bool on) {m_disableFiltered = on;}
|
||||||
void setFlags(DeviceModel::InterfaceFlags flags) {
|
void setFlags(DeviceModel::InterfaceFlags flags) {
|
||||||
m_flags = flags;
|
m_flags = flags;
|
||||||
invalidateFilter();
|
invalidateFilter();
|
||||||
@ -37,9 +39,12 @@ protected:
|
|||||||
const override {
|
const override {
|
||||||
return !(!m_showValueColumn && sourceColumn == DeviceModel::InterfaceColumn);
|
return !(!m_showValueColumn && sourceColumn == DeviceModel::InterfaceColumn);
|
||||||
}
|
}
|
||||||
|
// Optionally disable selection of items that don't have the wanted interface
|
||||||
|
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||||
// Overridden for showing items with children last/first
|
// Overridden for showing items with children last/first
|
||||||
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
|
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
|
||||||
private:
|
private:
|
||||||
|
bool m_disableFiltered;
|
||||||
bool m_showIcons;
|
bool m_showIcons;
|
||||||
bool m_showValueColumn;
|
bool m_showValueColumn;
|
||||||
DeviceModel::InterfaceFlags m_flags;
|
DeviceModel::InterfaceFlags m_flags;
|
||||||
|
106
src/tuxclocker-qt/data/DynamicReadableConnection.hpp
Normal file
106
src/tuxclocker-qt/data/DynamicReadableConnection.hpp
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "AssignableConnection.hpp"
|
||||||
|
#include <DynamicReadableProxy.hpp>
|
||||||
|
#include <fplus/fplus.hpp>
|
||||||
|
#include <patterns.hpp>
|
||||||
|
#include <QDBusVariant>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QPointF>
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QVector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
// delete these
|
||||||
|
using namespace fplus;
|
||||||
|
using namespace mpark::patterns;
|
||||||
|
using namespace TuxClocker::Device;
|
||||||
|
|
||||||
|
class DynamicReadableConnectionData {
|
||||||
|
public:
|
||||||
|
// y = f(x)
|
||||||
|
QVector<QPointF> points;
|
||||||
|
// DBus path
|
||||||
|
QString dynamicReadablePath;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: make DynamicReadableProxy type parametrized so we can be sure to only get numeric values from it
|
||||||
|
// Connection of an Assignable with a DynamicReadable
|
||||||
|
template <typename OutType> // Result of linear interpolation
|
||||||
|
class DynamicReadableConnection : public AssignableConnection {
|
||||||
|
public:
|
||||||
|
DynamicReadableConnection(DynamicReadableProxy &proxy,
|
||||||
|
QVector<QPointF> points, QObject *parent = nullptr)
|
||||||
|
: AssignableConnection(parent), m_proxy(proxy), m_points(points) {
|
||||||
|
auto sorted = sort_by([](auto point_l, auto point_r) {
|
||||||
|
return point_l.x() < point_r.x();
|
||||||
|
}, m_points);
|
||||||
|
m_points = sorted;
|
||||||
|
}
|
||||||
|
virtual QVariant connectionData() override {return QVariant();}
|
||||||
|
virtual void start() override {
|
||||||
|
/*QObject::connect(&m_timer, &QTimer::timeout, [this] {
|
||||||
|
int rand = QRandomGenerator::global()->bounded(0, 10);
|
||||||
|
|
||||||
|
QDBusVariant arg{QVariant{rand}};
|
||||||
|
QVariant v;
|
||||||
|
v.setValue(arg);
|
||||||
|
emit targetValueChanged(v, QString::number(rand));
|
||||||
|
});
|
||||||
|
m_timer.start(1000);*/
|
||||||
|
|
||||||
|
connect(&m_proxy, &DynamicReadableProxy::valueChanged, [this](auto val) {
|
||||||
|
match(val) (
|
||||||
|
pattern(as<ReadableValue>(arg)) = [this](auto rv) {
|
||||||
|
match(rv) (
|
||||||
|
pattern(as<uint>(arg)) = [this](auto u) {
|
||||||
|
emitTargetValue(u);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
/*QDBusVariant arg{QVariant{1}};
|
||||||
|
QVariant v;
|
||||||
|
v.setValue(arg);
|
||||||
|
emit targetValueChanged(v, "1");*/
|
||||||
|
|
||||||
|
}
|
||||||
|
virtual void stop() override {
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
DynamicReadableProxy &m_proxy;
|
||||||
|
QTimer m_timer;
|
||||||
|
QVector<QPointF> m_points;
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
void emitTargetValue(U reading) {
|
||||||
|
// Find two points from the vector so that:
|
||||||
|
// p[i].x < val < p[i + 1].x
|
||||||
|
std::optional<int> leftIndex = std::nullopt;
|
||||||
|
for (int i = 0; i < m_points.length() - 1; i++) {
|
||||||
|
if (m_points[i].x() < reading && reading < m_points[i + 1].x()) {
|
||||||
|
leftIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (leftIndex == std::nullopt)
|
||||||
|
return;
|
||||||
|
int li = leftIndex.value();
|
||||||
|
// What percentage the value is from dx of left and right interp points
|
||||||
|
double dx = m_points[li + 1].x() - m_points[li].x();
|
||||||
|
double dvx = reading - m_points[li].x();
|
||||||
|
double p = dvx / dx;
|
||||||
|
OutType interp_y = lerp(m_points[li].y(),
|
||||||
|
m_points[li + 1].y(), p);
|
||||||
|
QDBusVariant arg{QVariant{interp_y}};
|
||||||
|
QVariant v;
|
||||||
|
v.setValue(arg);
|
||||||
|
emit targetValueChanged(v, QString::number(interp_y));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T lerp(T a, T b, double t) {return a + (t * (b - a));}
|
||||||
|
};
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
MainWindow mw;
|
MainWindow mw;
|
||||||
mw.show();
|
mw.show();
|
||||||
|
|
||||||
|
@ -5,9 +5,11 @@ qt5_dep = dependency('qt5', modules: ['DBus', 'Charts', 'Widgets'])
|
|||||||
boost_dep = dependency('boost')
|
boost_dep = dependency('boost')
|
||||||
|
|
||||||
moc_files = qt5.preprocess(moc_headers: ['MainWindow.hpp',
|
moc_files = qt5.preprocess(moc_headers: ['MainWindow.hpp',
|
||||||
|
'data/AssignableConnection.hpp',
|
||||||
'data/AssignableItem.hpp',
|
'data/AssignableItem.hpp',
|
||||||
'data/AssignableProxy.hpp',
|
'data/AssignableProxy.hpp',
|
||||||
'data/DeviceModel.hpp',
|
'data/DeviceModel.hpp',
|
||||||
|
'data/DynamicReadableConnection.hpp',
|
||||||
'data/DynamicReadableProxy.hpp',
|
'data/DynamicReadableProxy.hpp',
|
||||||
'widgets/DragChartView.hpp'],
|
'widgets/DragChartView.hpp'],
|
||||||
qresources : ['resources/resources.qrc'],
|
qresources : ['resources/resources.qrc'],
|
||||||
@ -20,6 +22,7 @@ sources = ['main.cpp',
|
|||||||
'data/DeviceModelDelegate.cpp',
|
'data/DeviceModelDelegate.cpp',
|
||||||
'data/DeviceProxyModel.cpp',
|
'data/DeviceProxyModel.cpp',
|
||||||
'data/DynamicReadableProxy.cpp',
|
'data/DynamicReadableProxy.cpp',
|
||||||
|
'widgets/DeviceBrowser.cpp',
|
||||||
'widgets/DeviceTreeView.cpp',
|
'widgets/DeviceTreeView.cpp',
|
||||||
'widgets/DragChartView.cpp',
|
'widgets/DragChartView.cpp',
|
||||||
'widgets/EnumEditor.cpp',
|
'widgets/EnumEditor.cpp',
|
||||||
|
89
src/tuxclocker-qt/widgets/DeviceBrowser.cpp
Normal file
89
src/tuxclocker-qt/widgets/DeviceBrowser.cpp
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#include "DeviceBrowser.hpp"
|
||||||
|
#include "AssignableItemData.hpp"
|
||||||
|
#include "qnamespace.h"
|
||||||
|
|
||||||
|
#include <patterns.hpp>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
using namespace mpark::patterns;
|
||||||
|
using namespace TuxClocker::Device;
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(AssignableItemData)
|
||||||
|
Q_DECLARE_METATYPE(AssignableProxy*)
|
||||||
|
|
||||||
|
DeviceBrowser::DeviceBrowser(DeviceModel &model, QWidget *parent)
|
||||||
|
: QWidget(parent), m_deviceModel(model) {
|
||||||
|
m_layout = new QGridLayout(this);
|
||||||
|
m_proxyModel = new DeviceProxyModel(model, this);
|
||||||
|
m_treeView = new DeviceTreeView;
|
||||||
|
m_treeView->setModel(m_proxyModel);
|
||||||
|
m_flagLabel = new QLabel("Showing:");
|
||||||
|
m_apply = new QPushButton("Apply changes");
|
||||||
|
m_apply->setEnabled(true);
|
||||||
|
|
||||||
|
m_flagEditor = new FlagEditor(
|
||||||
|
QVector({
|
||||||
|
std::tuple(
|
||||||
|
QString("Assignables"),
|
||||||
|
DeviceModel::assignableIcon(),
|
||||||
|
DeviceModel::Assignable
|
||||||
|
),
|
||||||
|
std::tuple(
|
||||||
|
QString("Dynamic Values"),
|
||||||
|
DeviceModel::dynamicReadableIcon(),
|
||||||
|
DeviceModel::DynamicReadable
|
||||||
|
),
|
||||||
|
std::tuple(
|
||||||
|
QString("Static Values"),
|
||||||
|
DeviceModel::staticReadableIcon(),
|
||||||
|
DeviceModel::StaticReadable
|
||||||
|
)
|
||||||
|
}), this);
|
||||||
|
|
||||||
|
connect(m_apply, &QPushButton::pressed, &m_deviceModel,
|
||||||
|
&DeviceModel::applyChanges);
|
||||||
|
|
||||||
|
m_treeView->functionEditorRequested.connect([this](QModelIndex &index) {
|
||||||
|
auto a_data = index.data(DeviceModel::AssignableRole);
|
||||||
|
if (!a_data.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto a_info = a_data.value<AssignableItemData>();
|
||||||
|
auto proxy = index.data(DeviceModel::AssignableProxyRole)
|
||||||
|
.value<AssignableProxy*>();
|
||||||
|
auto name = index.data(DeviceModel::NodeNameRole).toString();
|
||||||
|
match(a_info.assignableInfo()) (
|
||||||
|
pattern(as<RangeInfo>(arg)) = [=](auto ri) {
|
||||||
|
auto f_editor = new FunctionEditor{m_deviceModel,
|
||||||
|
ri, *proxy, name};
|
||||||
|
f_editor->show();
|
||||||
|
f_editor->assignableConnectionChanged
|
||||||
|
.connect([=](auto conn) {
|
||||||
|
proxy->startConnection(conn);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
pattern(_) = [] {}
|
||||||
|
);
|
||||||
|
|
||||||
|
/*auto f_editor = new FunctionEditor(m_deviceModel, rangeInfo, proxy);
|
||||||
|
f_editor->show();
|
||||||
|
|
||||||
|
f_editor->assignableConnectionChanged.connect(
|
||||||
|
[&proxy] (auto assignableConnection) {
|
||||||
|
proxy.startConnection(assignableConnection);
|
||||||
|
});*/
|
||||||
|
});
|
||||||
|
|
||||||
|
m_flagEditor->setFlags(DeviceModel::AllInterfaces);
|
||||||
|
|
||||||
|
m_flagEditor->flagsChanged.connect([=](auto flags) {
|
||||||
|
m_proxyModel->setFlags(flags);
|
||||||
|
});
|
||||||
|
|
||||||
|
m_layout->addWidget(m_flagLabel, 0, 0);
|
||||||
|
m_layout->addWidget(m_flagEditor, 0, 1);
|
||||||
|
m_layout->addWidget(m_treeView, 1, 0, 1, 2);
|
||||||
|
m_layout->addWidget(m_apply, 2, 0, 1, 2);
|
||||||
|
|
||||||
|
setLayout(m_layout);
|
||||||
|
}
|
@ -4,7 +4,7 @@
|
|||||||
#include <DeviceModel.hpp>
|
#include <DeviceModel.hpp>
|
||||||
#include <DeviceProxyModel.hpp>
|
#include <DeviceProxyModel.hpp>
|
||||||
#include <FlagEditor.hpp>
|
#include <FlagEditor.hpp>
|
||||||
//#include <FunctionEditor.hpp>
|
#include <FunctionEditor.hpp>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QGridLayout>
|
#include <QGridLayout>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
@ -14,57 +14,7 @@
|
|||||||
// Class for viewing/editing the main tuxclocker tree
|
// Class for viewing/editing the main tuxclocker tree
|
||||||
class DeviceBrowser : public QWidget {
|
class DeviceBrowser : public QWidget {
|
||||||
public:
|
public:
|
||||||
DeviceBrowser(DeviceModel &model, QWidget *parent = nullptr)
|
DeviceBrowser(DeviceModel &model, QWidget *parent = nullptr);
|
||||||
: QWidget(parent), m_deviceModel(model) {
|
|
||||||
m_layout = new QGridLayout(this);
|
|
||||||
m_proxyModel = new DeviceProxyModel(model, this);
|
|
||||||
m_treeView = new DeviceTreeView;
|
|
||||||
m_treeView->setModel(m_proxyModel);
|
|
||||||
m_flagLabel = new QLabel("Showing:");
|
|
||||||
m_apply = new QPushButton("Apply changes");
|
|
||||||
m_apply->setEnabled(true);
|
|
||||||
|
|
||||||
m_flagEditor = new FlagEditor(
|
|
||||||
QVector({
|
|
||||||
std::tuple(
|
|
||||||
QString("Assignables"),
|
|
||||||
DeviceModel::assignableIcon(),
|
|
||||||
DeviceModel::Assignable
|
|
||||||
),
|
|
||||||
std::tuple(
|
|
||||||
QString("Dynamic Values"),
|
|
||||||
DeviceModel::dynamicReadableIcon(),
|
|
||||||
DeviceModel::DynamicReadable
|
|
||||||
),
|
|
||||||
std::tuple(
|
|
||||||
QString("Static Values"),
|
|
||||||
DeviceModel::staticReadableIcon(),
|
|
||||||
DeviceModel::StaticReadable
|
|
||||||
)
|
|
||||||
}), this);
|
|
||||||
|
|
||||||
connect(m_apply, &QPushButton::pressed, &m_deviceModel,
|
|
||||||
&DeviceModel::applyChanges);
|
|
||||||
|
|
||||||
m_treeView->functionEditorRequested.connect([this] {
|
|
||||||
//auto f_editor = new FunctionEditor(m_deviceModel);
|
|
||||||
//f_editor->show();
|
|
||||||
});
|
|
||||||
|
|
||||||
m_flagEditor->setFlags(DeviceModel::AllInterfaces);
|
|
||||||
|
|
||||||
m_flagEditor->flagsChanged.connect([=](auto flags) {
|
|
||||||
m_proxyModel->setFlags(flags);
|
|
||||||
});
|
|
||||||
|
|
||||||
m_layout->addWidget(m_flagLabel, 0, 0);
|
|
||||||
m_layout->addWidget(m_flagEditor, 0, 1);
|
|
||||||
m_layout->addWidget(m_treeView, 1, 0, 1, 2);
|
|
||||||
m_layout->addWidget(m_apply, 2, 0, 1, 2);
|
|
||||||
|
|
||||||
setLayout(m_layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DeviceModel &m_deviceModel;
|
DeviceModel &m_deviceModel;
|
||||||
DeviceProxyModel *m_proxyModel;
|
DeviceProxyModel *m_proxyModel;
|
||||||
|
@ -1,45 +1,50 @@
|
|||||||
#include "DeviceTreeView.hpp"
|
#include "DeviceTreeView.hpp"
|
||||||
|
#include "qcheckbox.h"
|
||||||
|
|
||||||
#include <DragChartView.hpp>
|
#include <DragChartView.hpp>
|
||||||
//#include <FunctionEditor.hpp>
|
#include <patterns.hpp>
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QHeaderView>
|
||||||
|
|
||||||
|
using namespace mpark::patterns;
|
||||||
|
using namespace TuxClocker::Device;
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(AssignableItemData)
|
Q_DECLARE_METATYPE(AssignableItemData)
|
||||||
|
Q_DECLARE_METATYPE(AssignableProxy*)
|
||||||
|
|
||||||
DeviceTreeView::DeviceTreeView(QWidget *parent)
|
DeviceTreeView::DeviceTreeView(QWidget *parent)
|
||||||
: QTreeView(parent) {
|
: QTreeView(parent) {
|
||||||
|
header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||||
setSortingEnabled(true);
|
setSortingEnabled(true);
|
||||||
auto triggers = editTriggers() ^= DoubleClicked;
|
|
||||||
triggers |= SelectedClicked;
|
|
||||||
setEditTriggers(SelectedClicked | EditKeyPressed);
|
setEditTriggers(SelectedClicked | EditKeyPressed);
|
||||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
connect(this, &QTreeView::customContextMenuRequested, [this](QPoint point) {
|
connect(this, &QTreeView::customContextMenuRequested, [this](QPoint point) {
|
||||||
auto index = indexAt(point);
|
auto index = indexAt(point);
|
||||||
auto data = index.data(DeviceModel::AssignableRole);
|
auto data = index.data(DeviceModel::AssignableRole);
|
||||||
|
auto proxyData = index.data(DeviceModel::AssignableProxyRole);
|
||||||
QMenu menu;
|
QMenu menu;
|
||||||
if (data.canConvert<AssignableItemData>()) {
|
QCheckBox checkBox("Enable connection");
|
||||||
|
QAction editConn("Edit connection...");
|
||||||
|
auto enableConn = new QWidgetAction(&menu);
|
||||||
|
enableConn->setDefaultWidget(&checkBox);
|
||||||
|
menu.addActions({&editConn, enableConn});
|
||||||
|
if (data.canConvert<AssignableItemData>() &&
|
||||||
|
proxyData.canConvert<AssignableProxy*>()) {
|
||||||
|
functionEditorRequested(index);
|
||||||
/*auto a_data = data.value<AssignableItemData>();
|
/*auto a_data = data.value<AssignableItemData>();
|
||||||
QCheckBox commitCb("Commit");
|
auto proxy = proxyData.value<AssignableProxy*>();
|
||||||
auto commitAct = new QWidgetAction(&menu);
|
match(a_data.assignableInfo()) (
|
||||||
commitAct->setDefaultWidget(&commitCb);
|
pattern(as<RangeInfo>(arg)) = [this, &menu, proxy, &editConn](auto ri) {
|
||||||
commitCb.setChecked(a_data.committal());
|
//functionEditorRequested(*proxy, ri);
|
||||||
connect(&commitCb, &QCheckBox::toggled, [&](bool toggled) {
|
connect(&editConn, &QAction::triggered, [this, proxy, ri] {
|
||||||
a_data.setCommittal(toggled);
|
functionEditorRequested(*proxy, ri);
|
||||||
});
|
});
|
||||||
// Write the committal value to the model only on menu close
|
menu.exec(QCursor::pos());
|
||||||
connect(&menu, &QMenu::aboutToHide, [&] {
|
},
|
||||||
QVariant v;
|
pattern(_) = []{}
|
||||||
v.setValue(a_data);
|
);*/
|
||||||
m_deviceModel.setData(index, v, DeviceModel::AssignableRole);
|
|
||||||
});
|
|
||||||
menu.addAction(commitAct);
|
|
||||||
menu.exec(QCursor::pos());*/
|
|
||||||
|
|
||||||
//auto dragView = new FunctionEditor;
|
|
||||||
//dragView->show();
|
|
||||||
|
|
||||||
functionEditorRequested();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
m_delegate = new DeviceModelDelegate(this);
|
m_delegate = new DeviceModelDelegate(this);
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AssignableItemData.hpp>
|
#include <AssignableItemData.hpp>
|
||||||
|
#include <AssignableProxy.hpp>
|
||||||
#include <boost/signals2.hpp>
|
#include <boost/signals2.hpp>
|
||||||
|
#include <Device.hpp>
|
||||||
#include <DeviceModel.hpp>
|
#include <DeviceModel.hpp>
|
||||||
#include <DeviceModelDelegate.hpp>
|
#include <DeviceModelDelegate.hpp>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
@ -14,7 +16,9 @@ public:
|
|||||||
DeviceTreeView(QWidget *parent = nullptr);
|
DeviceTreeView(QWidget *parent = nullptr);
|
||||||
// Accessor method for connecting everything in the browser
|
// Accessor method for connecting everything in the browser
|
||||||
//const DeviceModel &deviceModel() {return m_deviceModel;}
|
//const DeviceModel &deviceModel() {return m_deviceModel;}
|
||||||
boost::signals2::signal<void()> functionEditorRequested;
|
// TODO: make this more generalized
|
||||||
|
// Defers the complexity to DeviceBrowser
|
||||||
|
boost::signals2::signal<void(QModelIndex&)> functionEditorRequested;
|
||||||
protected:
|
protected:
|
||||||
/* Workaround for the retarded behavior of waiting for a double click,
|
/* Workaround for the retarded behavior of waiting for a double click,
|
||||||
you can't even disable it! */
|
you can't even disable it! */
|
||||||
|
@ -176,6 +176,12 @@ void DragChartView::setVector(const QVector <QPointF> vector) {
|
|||||||
m_series.replace(vector);
|
m_series.replace(vector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DragChartView::setRange(qreal xmin, qreal xmax, qreal ymin, qreal ymax) {
|
||||||
|
m_series.clear();
|
||||||
|
m_xAxis.setRange(xmin, xmax);
|
||||||
|
m_yAxis.setRange(ymin, ymax);
|
||||||
|
}
|
||||||
|
|
||||||
bool DragChartView::event(QEvent *event) {
|
bool DragChartView::event(QEvent *event) {
|
||||||
//qDebug() << event->type();
|
//qDebug() << event->type();
|
||||||
|
|
||||||
@ -227,7 +233,10 @@ void DragChartView::mouseMoveEvent(QMouseEvent *event) {
|
|||||||
m_toolTipLabel->show();
|
m_toolTipLabel->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_toolTipLabel->setText(QString("%1, %2").arg(QString::number(m_latestScatterPoint.x()), QString::number(m_latestScatterPoint.y())));
|
m_toolTipLabel->setText(QString("%1, %2")
|
||||||
|
.arg(QString::number(m_latestScatterPoint.x()),
|
||||||
|
QString::number(m_latestScatterPoint.y())));
|
||||||
|
|
||||||
// FIXME : doesn't work properly when screen is switched(?)
|
// FIXME : doesn't work properly when screen is switched(?)
|
||||||
m_toolTipLabel->move(event->screenPos().toPoint() + toolTipOffset(this, event->windowPos().toPoint()));
|
m_toolTipLabel->move(event->screenPos().toPoint() + toolTipOffset(this, event->windowPos().toPoint()));
|
||||||
|
|
||||||
@ -324,14 +333,17 @@ void DragChartView::drawFillerLines(QScatterSeries *series) {
|
|||||||
|
|
||||||
for (int i = 0; i < sorted.length() - 1; i++) {
|
for (int i = 0; i < sorted.length() - 1; i++) {
|
||||||
m_lineFillerItems[i]->setLine(QLineF(chart()->mapToPosition(sorted[i]),
|
m_lineFillerItems[i]->setLine(QLineF(chart()->mapToPosition(sorted[i]),
|
||||||
chart()->mapToPosition(sorted[i + 1])));
|
chart()->mapToPosition(sorted[i + 1])));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_leftLineFillerItem->setLine(QLineF(chart()->mapToPosition(QPointF(m_xAxis.min(), sorted[0].y())),
|
m_leftLineFillerItem->setLine(
|
||||||
chart()->mapToPosition(sorted[0])));
|
QLineF(chart()->mapToPosition(QPointF(
|
||||||
|
m_xAxis.min(), sorted[0].y())),
|
||||||
|
chart()->mapToPosition(sorted[0])));
|
||||||
|
|
||||||
m_rightLineFillerItem->setLine(QLineF(chart()->mapToPosition(sorted.last()),
|
m_rightLineFillerItem->setLine(
|
||||||
chart()->mapToPosition(QPointF(m_xAxis.max(), sorted.last().y()))));
|
QLineF(chart()->mapToPosition(sorted.last()),
|
||||||
|
chart()->mapToPosition(QPointF(m_xAxis.max(), sorted.last().y()))));
|
||||||
|
|
||||||
chart()->update();
|
chart()->update();
|
||||||
}
|
}
|
||||||
|
@ -10,11 +10,13 @@ using namespace QtCharts;
|
|||||||
class DragChartView : public QChartView {
|
class DragChartView : public QChartView {
|
||||||
public:
|
public:
|
||||||
DragChartView(QWidget *parent = nullptr);
|
DragChartView(QWidget *parent = nullptr);
|
||||||
QValueAxis *xAxis() {return &m_xAxis;}
|
QValueAxis &xAxis() {return m_xAxis;}
|
||||||
QValueAxis *yAxis() {return &m_yAxis;}
|
QValueAxis &yAxis() {return m_yAxis;}
|
||||||
|
|
||||||
void setVector(const QVector <QPointF> vector);
|
void setVector(const QVector <QPointF> vector);
|
||||||
QVector <QPointF> vector() {return m_series.pointsVector();}
|
QVector <QPointF> vector() {return m_series.pointsVector();}
|
||||||
|
// Clear the points and set new range
|
||||||
|
void setRange(qreal xmin, qreal xmax, qreal ymin, qreal ymax);
|
||||||
protected:
|
protected:
|
||||||
bool event(QEvent*);
|
bool event(QEvent*);
|
||||||
void mousePressEvent(QMouseEvent*);
|
void mousePressEvent(QMouseEvent*);
|
||||||
|
@ -94,6 +94,9 @@ protected:
|
|||||||
QStylePainter painter(this);
|
QStylePainter painter(this);
|
||||||
QStyleOptionComboBox opt;
|
QStyleOptionComboBox opt;
|
||||||
initStyleOption(&opt);
|
initStyleOption(&opt);
|
||||||
|
QPalette rp;
|
||||||
|
rp.setColor(QPalette::Highlight, Qt::red);
|
||||||
|
opt.palette = rp;
|
||||||
|
|
||||||
// Show which flags are selected
|
// Show which flags are selected
|
||||||
auto items = m_flagHash.values();
|
auto items = m_flagHash.values();
|
||||||
|
122
src/tuxclocker-qt/widgets/FunctionEditor.hpp
Normal file
122
src/tuxclocker-qt/widgets/FunctionEditor.hpp
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/* Widget for editing the function for an assignable.
|
||||||
|
Possible options, where y = value of assignable, t = time,
|
||||||
|
x = value of readable, n = static value set in model editor:
|
||||||
|
- y = n
|
||||||
|
- y = f(x)
|
||||||
|
- y = f(t)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "DynamicReadableProxy.hpp"
|
||||||
|
#include "qnamespace.h"
|
||||||
|
#include <AssignableConnection.hpp>
|
||||||
|
#include <AssignableProxy.hpp>
|
||||||
|
#include <boost/signals2.hpp>
|
||||||
|
#include <DeviceProxyModel.hpp>
|
||||||
|
#include <DragChartView.hpp>
|
||||||
|
#include <DynamicReadableConnection.hpp>
|
||||||
|
#include <NodeSelector.hpp>
|
||||||
|
#include <patterns.hpp>
|
||||||
|
#include <QAbstractItemView>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QGridLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QModelIndex>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
// Delet this
|
||||||
|
namespace p = mpark::patterns;
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(DynamicReadableProxy*)
|
||||||
|
|
||||||
|
// TODO: make constructor of the type data Editor a = Maybe (Range a)
|
||||||
|
class FunctionEditor : public QWidget {
|
||||||
|
public:
|
||||||
|
FunctionEditor(DeviceModel &model, TuxClocker::Device::RangeInfo rangeInfo,
|
||||||
|
AssignableProxy &proxy, QString nodeName,
|
||||||
|
QWidget *parent = nullptr)
|
||||||
|
: QWidget(parent), m_assignableProxy(proxy),
|
||||||
|
m_model(model), m_proxyModel(model),
|
||||||
|
m_rangeInfo(rangeInfo) {
|
||||||
|
m_proxyModel.setDisableFiltered(true);
|
||||||
|
m_proxyModel.setFlags(DeviceModel::DynamicReadable);
|
||||||
|
m_proxyModel.setShowIcons(false);
|
||||||
|
//m_proxyModel.setShowValueColumn(false);
|
||||||
|
m_layout = new QGridLayout(this);
|
||||||
|
m_functionComboBox = new QComboBox;
|
||||||
|
m_functionComboBox->addItem("Function of time");
|
||||||
|
m_dependableReadableComboBox = new NodeSelector;
|
||||||
|
auto treeView = new QTreeView;
|
||||||
|
m_dependableReadableComboBox->setModel(&m_proxyModel);
|
||||||
|
m_dependableReadableComboBox->setView(treeView);
|
||||||
|
treeView->expandAll();
|
||||||
|
m_dependableLabel = new QLabel("Connecting with:");
|
||||||
|
m_layout->addWidget(m_dependableReadableComboBox, 0, 0);
|
||||||
|
m_layout->addWidget(m_functionComboBox, 0, 1);
|
||||||
|
m_dragView = new DragChartView;
|
||||||
|
|
||||||
|
p::match(rangeInfo) (
|
||||||
|
p::pattern(p::as<TuxClocker::Device::Range<double>>(p::arg))
|
||||||
|
= [this](auto dr) {
|
||||||
|
m_dragView->setRange(0, 100, dr.min, dr.max);
|
||||||
|
},
|
||||||
|
p::pattern(p::as<TuxClocker::Device::Range<int>>(p::arg))
|
||||||
|
= [this](auto ir) {
|
||||||
|
m_dragView->setRange(0, 100, ir.min, ir.max);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
//m_dragView->setRange(0, 100, 0, 100);
|
||||||
|
m_layout->addWidget(m_dragView, 1, 0, 1, 2);
|
||||||
|
m_applyButton = new QPushButton("Apply");
|
||||||
|
// No connection to apply at first
|
||||||
|
m_applyButton->setEnabled(false);
|
||||||
|
m_cancelButton = new QPushButton("Cancel");
|
||||||
|
m_layout->addWidget(m_cancelButton, 2, 0, 1, 1);
|
||||||
|
m_layout->addWidget(m_applyButton, 2, 1, 1, 1);
|
||||||
|
|
||||||
|
connect(m_applyButton, &QPushButton::clicked, [this] {
|
||||||
|
auto proxy = m_latestNodeIndex
|
||||||
|
.data(DeviceModel::DynamicReadableProxyRole)
|
||||||
|
.value<DynamicReadableProxy*>();
|
||||||
|
//qDebug() << proxy;
|
||||||
|
auto points = m_dragView->vector();
|
||||||
|
if (points.length() < 2)
|
||||||
|
return;
|
||||||
|
//qDebug() << points;
|
||||||
|
auto conn = std::make_shared<DynamicReadableConnection<int>>(
|
||||||
|
*proxy, points);
|
||||||
|
assignableConnectionChanged(conn);
|
||||||
|
});
|
||||||
|
|
||||||
|
m_dragView->yAxis().setTitleText(nodeName);
|
||||||
|
|
||||||
|
m_dependableReadableComboBox->indexChanged
|
||||||
|
.connect([this](auto &index) {
|
||||||
|
m_latestNodeIndex = index;
|
||||||
|
m_applyButton->setEnabled(true);
|
||||||
|
auto nodeName = index.data(Qt::DisplayRole).toString();
|
||||||
|
m_dragView->xAxis().setTitleText(nodeName);
|
||||||
|
});
|
||||||
|
|
||||||
|
setLayout(m_layout);
|
||||||
|
}
|
||||||
|
boost::signals2::signal<void(std::shared_ptr<AssignableConnection>)>
|
||||||
|
assignableConnectionChanged;
|
||||||
|
private:
|
||||||
|
AssignableProxy &m_assignableProxy;
|
||||||
|
DeviceModel &m_model;
|
||||||
|
DeviceProxyModel m_proxyModel;
|
||||||
|
DragChartView *m_dragView;
|
||||||
|
//NodeSelector *m_nodeSelector;
|
||||||
|
QComboBox *m_functionComboBox;
|
||||||
|
NodeSelector *m_dependableReadableComboBox;
|
||||||
|
QGridLayout *m_layout;
|
||||||
|
QLabel *m_dependableLabel;
|
||||||
|
QModelIndex m_latestNodeIndex;
|
||||||
|
QPushButton *m_applyButton, *m_cancelButton;
|
||||||
|
TuxClocker::Device::RangeInfo m_rangeInfo;
|
||||||
|
};
|
65
src/tuxclocker-qt/widgets/NodeSelector.hpp
Normal file
65
src/tuxclocker-qt/widgets/NodeSelector.hpp
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/signals2.hpp>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QEvent>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QScrollBar>
|
||||||
|
|
||||||
|
// TODO: If a disabled item is clicked, the item that is highlighted get selected instead
|
||||||
|
class NodeSelector : public QComboBox {
|
||||||
|
public:
|
||||||
|
NodeSelector(QWidget *parent = nullptr) : QComboBox(parent),
|
||||||
|
m_skipNextHide(false) {
|
||||||
|
view()->viewport()->installEventFilter(this);
|
||||||
|
}
|
||||||
|
bool eventFilter(QObject *obj, QEvent *ev) {
|
||||||
|
if (ev->type() == QEvent::MouseButtonPress && obj == view()->viewport()) {
|
||||||
|
auto mouse_ev = static_cast<QMouseEvent*>(ev);
|
||||||
|
auto index = view()->indexAt(mouse_ev->pos());
|
||||||
|
// Hide popup when an enabled item is clicked
|
||||||
|
if (!view()->visualRect(index).contains(mouse_ev->pos()) ||
|
||||||
|
!(model()->flags(index) & Qt::ItemIsEnabled))
|
||||||
|
m_skipNextHide = true;
|
||||||
|
else if (model()->flags(index) & Qt::ItemIsSelectable) {
|
||||||
|
qDebug() << index.data(DeviceModel::DynamicReadableProxyRole);
|
||||||
|
indexChanged(index);
|
||||||
|
m_skipNextHide = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QComboBox::eventFilter(obj, ev);
|
||||||
|
}
|
||||||
|
virtual void hidePopup() override {
|
||||||
|
if (m_skipNextHide)
|
||||||
|
m_skipNextHide = false;
|
||||||
|
else
|
||||||
|
QComboBox::hidePopup();
|
||||||
|
}
|
||||||
|
/*void mouseReleaseEvent(QMouseEvent *event) override {
|
||||||
|
auto index = view()->indexAt(event->pos());
|
||||||
|
if (!view()->visualRect(index).contains(event->pos()) ||
|
||||||
|
!(model()->flags(index) & Qt::ItemIsEnabled))
|
||||||
|
qDebug() << index.data();
|
||||||
|
|
||||||
|
QComboBox::mouseReleaseEvent(event);
|
||||||
|
}*/
|
||||||
|
void setView(QAbstractItemView *view) {
|
||||||
|
// Why no signal, Qt?
|
||||||
|
view->viewport()->installEventFilter(this);
|
||||||
|
|
||||||
|
QComboBox::setView(view);
|
||||||
|
}
|
||||||
|
virtual void showPopup() override {
|
||||||
|
// TODO: don't account for scrollbar width when it isn't visible
|
||||||
|
// This is quite a weird way to do it but it works
|
||||||
|
auto vBarWidth =
|
||||||
|
QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent);
|
||||||
|
view()->setMinimumWidth(view()->sizeHintForColumn(0) + vBarWidth);
|
||||||
|
QComboBox::showPopup();
|
||||||
|
}
|
||||||
|
boost::signals2::signal<void(QModelIndex&)> indexChanged;
|
||||||
|
private:
|
||||||
|
bool m_skipNextHide;
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user