diff --git a/gr-util/src/grdeviceaddon.cpp b/gr-util/src/grdeviceaddon.cpp index 45bc945e39..3b4af9383b 100644 --- a/gr-util/src/grdeviceaddon.cpp +++ b/gr-util/src/grdeviceaddon.cpp @@ -27,7 +27,7 @@ QWidget *GRDeviceAddon::createAttrMenu(QWidget *parent) MenuSectionWidget *attrContainer = new MenuSectionWidget(parent); MenuCollapseSection *attr = new MenuCollapseSection("ATTRIBUTES", MenuCollapseSection::MHCW_NONE, attrContainer); - QList attrWidgets = IIOWidgetBuilder().device(m_src->iioDev()).buildAll(); + QList attrWidgets = IIOWidgetBuilder().device(m_src->iioDev()).parent(parent).buildAll(); const struct iio_context *ctx = iio_device_get_context(m_src->iioDev()); attrWidgets.append(IIOWidgetBuilder() .context(const_cast(ctx)) @@ -35,6 +35,7 @@ QWidget *GRDeviceAddon::createAttrMenu(QWidget *parent) .attribute("Triggers") .uiStrategy(IIOWidgetBuilder::UIS::ComboUi) .dataStrategy(IIOWidgetBuilder::DS::TriggerData) + .parent(parent) .buildSingle()); auto layout = new QVBoxLayout(); diff --git a/gr-util/src/grtimechanneladdon.cpp b/gr-util/src/grtimechanneladdon.cpp index 4b74e90178..0a60a2ac45 100644 --- a/gr-util/src/grtimechanneladdon.cpp +++ b/gr-util/src/grtimechanneladdon.cpp @@ -193,7 +193,7 @@ QWidget *GRTimeChannelAddon::createAttrMenu(QWidget *parent) MenuSectionWidget *attrcontainer = new MenuSectionWidget(parent); MenuCollapseSection *attr = new MenuCollapseSection("ATTRIBUTES", MenuCollapseSection::MHCW_NONE, attrcontainer); - QList attrWidgets = IIOWidgetBuilder().channel(grch()->channel()).buildAll(); + QList attrWidgets = IIOWidgetBuilder().channel(grch()->channel()).parent(parent).buildAll(); auto layout = new QVBoxLayout(); layout->setSpacing(10); diff --git a/gui/include/gui/widgets/menucombo.h b/gui/include/gui/widgets/menucombo.h index fee949df32..75ab9e7948 100644 --- a/gui/include/gui/widgets/menucombo.h +++ b/gui/include/gui/widgets/menucombo.h @@ -19,6 +19,7 @@ class SCOPY_GUI_EXPORT MenuCombo : public QWidget MenuCombo(QString title, QWidget *parent = nullptr); virtual ~MenuCombo(); + QLabel *label(); QComboBox *combo(); void applyStylesheet(); diff --git a/gui/src/widgets/menucombo.cpp b/gui/src/widgets/menucombo.cpp index cf8195c62b..1f59c2cf4c 100644 --- a/gui/src/widgets/menucombo.cpp +++ b/gui/src/widgets/menucombo.cpp @@ -46,6 +46,8 @@ MenuCombo::MenuCombo(QString title, QWidget *parent) } MenuCombo::~MenuCombo() {} + +QLabel *MenuCombo::label() { return m_label; } QComboBox *MenuCombo::combo() { return m_combo; } void MenuCombo::applyStylesheet() diff --git a/iio-widgets/include/iio-widgets/guistrategy/comboguistrategy.h b/iio-widgets/include/iio-widgets/guistrategy/comboguistrategy.h index 60a7647401..c5da74f34a 100644 --- a/iio-widgets/include/iio-widgets/guistrategy/comboguistrategy.h +++ b/iio-widgets/include/iio-widgets/guistrategy/comboguistrategy.h @@ -49,6 +49,7 @@ class SCOPY_IIO_WIDGETS_EXPORT ComboAttrUi : public QWidget, public GuiStrategyI public Q_SLOTS: void receiveData(QString currentData, QString optionalData) override; + void changeName(QString name) override; Q_SIGNALS: void displayedNewData(QString data, QString optionalData) override; @@ -58,6 +59,8 @@ public Q_SLOTS: private: QWidget *m_ui; QComboBox *m_comboWidget; + MenuCombo *m_menuCombo; + QLabel *m_compactLabel; bool m_isCompact; }; } // namespace scopy diff --git a/iio-widgets/include/iio-widgets/guistrategy/editableguistrategy.h b/iio-widgets/include/iio-widgets/guistrategy/editableguistrategy.h index 03f478f8f7..f02d7883c9 100644 --- a/iio-widgets/include/iio-widgets/guistrategy/editableguistrategy.h +++ b/iio-widgets/include/iio-widgets/guistrategy/editableguistrategy.h @@ -49,6 +49,7 @@ class SCOPY_IIO_WIDGETS_EXPORT EditableGuiStrategy : public QWidget, public GuiS public Q_SLOTS: void receiveData(QString currentData, QString optionalData) override; + void changeName(QString name) override; Q_SIGNALS: void displayedNewData(QString data, QString optionalData) override; @@ -57,6 +58,7 @@ public Q_SLOTS: private: QWidget *m_ui; + QLabel *m_titleLabel; MenuLineEdit *m_lineEdit; QString m_lastEmittedText; }; diff --git a/iio-widgets/include/iio-widgets/guistrategy/guistrategyinterface.h b/iio-widgets/include/iio-widgets/guistrategy/guistrategyinterface.h index 06f7fbcbe7..8c7b9b71f4 100644 --- a/iio-widgets/include/iio-widgets/guistrategy/guistrategyinterface.h +++ b/iio-widgets/include/iio-widgets/guistrategy/guistrategyinterface.h @@ -56,6 +56,12 @@ public Q_SLOTS: * */ virtual void receiveData(QString currentData, QString optionalData) = 0; + /** + * @brief changeName Changes the title of the UI strategy + * @param name The new name/title of the UI strategy + */ + virtual void changeName(QString name) = 0; + Q_SIGNALS: /** * @brief This signal is emitted when the ui strategy receives new data from external sources, diff --git a/iio-widgets/include/iio-widgets/guistrategy/rangeguistrategy.h b/iio-widgets/include/iio-widgets/guistrategy/rangeguistrategy.h index 6d1261a6d7..e9d03a49aa 100644 --- a/iio-widgets/include/iio-widgets/guistrategy/rangeguistrategy.h +++ b/iio-widgets/include/iio-widgets/guistrategy/rangeguistrategy.h @@ -52,6 +52,7 @@ class SCOPY_IIO_WIDGETS_EXPORT RangeAttrUi : public QWidget, public GuiStrategyI public Q_SLOTS: void receiveData(QString currentData, QString optionalData) override; + void changeName(QString name) override; Q_SIGNALS: void displayedNewData(QString data, QString optionalData) override; diff --git a/iio-widgets/include/iio-widgets/guistrategy/switchguistrategy.h b/iio-widgets/include/iio-widgets/guistrategy/switchguistrategy.h index 06b07c3325..04d16cf1d7 100644 --- a/iio-widgets/include/iio-widgets/guistrategy/switchguistrategy.h +++ b/iio-widgets/include/iio-widgets/guistrategy/switchguistrategy.h @@ -50,6 +50,7 @@ class SCOPY_IIO_WIDGETS_EXPORT SwitchAttrUi : public QWidget, public GuiStrategy public Q_SLOTS: void receiveData(QString currentData, QString optionalData) override; + void changeName(QString name) override; Q_SIGNALS: void displayedNewData(QString data, QString optionalData) override; diff --git a/iio-widgets/include/iio-widgets/iioconfigurationpopup.h b/iio-widgets/include/iio-widgets/iioconfigurationpopup.h new file mode 100644 index 0000000000..bcce6ac46a --- /dev/null +++ b/iio-widgets/include/iio-widgets/iioconfigurationpopup.h @@ -0,0 +1,46 @@ +#ifndef IIOCONFIGURATIONPOPUP_H +#define IIOCONFIGURATIONPOPUP_H + +#include "iiowidgetselector.h" +#include "scopy-iio-widgets_export.h" +#include +#include +#include +#include +#include +#include +#include + +namespace scopy { +class SCOPY_IIO_WIDGETS_EXPORT IIOConfigurationPopup : public QWidget +{ + Q_OBJECT +public: + explicit IIOConfigurationPopup(iio_context *ctx, QWidget *parent = nullptr); + explicit IIOConfigurationPopup(iio_device *dev, QWidget *parent = nullptr); + explicit IIOConfigurationPopup(iio_channel *chnl, QWidget *parent = nullptr); + ~IIOConfigurationPopup(); + + void enableTintedOverlay(bool enable = true); + +Q_SIGNALS: + void selectButtonClicked(IIOItem *selected); + void exitButtonClicked(); + +protected Q_SLOTS: + void modelSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + +protected: + void init(); + void initUI(); + + gui::TintedOverlay *m_tintedOverlay; + QLabel *m_titleLabel; + IIOWidgetSelector *m_widgetSelector; + QPushButton *m_exitButton; + QPushButton *m_selectButton; + IIOItem *m_root; +}; +} // namespace scopy + +#endif // IIOCONFIGURATIONPOPUP_H diff --git a/iio-widgets/include/iio-widgets/iiowidget.h b/iio-widgets/include/iio-widgets/iiowidget.h index 0fafe8bbee..83cb07ddd2 100644 --- a/iio-widgets/include/iio-widgets/iiowidget.h +++ b/iio-widgets/include/iio-widgets/iiowidget.h @@ -22,11 +22,14 @@ #define SCOPY_IIOWIDGET_H #include +#include "iioconfigurationpopup.h" #include "iiowidgetdata.h" #include "utils.h" #include #include #include +#include +#include #include "guistrategy/guistrategyinterface.h" #include "datastrategy/datastrategyinterface.h" #include "scopy-iio-widgets_export.h" @@ -81,16 +84,11 @@ class SCOPY_IIO_WIDGETS_EXPORT IIOWidget : public QWidget void writeAsync(QString data); /** - * @brief Returns the UI of the IIOWidget - * @return GuiStrategyInterface - * */ - GuiStrategyInterface *getUiStrategy(); - - /** - * @brief Returns the data save/load strategy - * @return DataStretegyInterface - * */ - DataStrategyInterface *getDataStrategy(); + * @brief swapDataStrategy Disconnects the current Data Strategy and connects the new Data Strategy. + * @param dataStrategy The new Data Strategy that will be connected to the UIC. + * @return The old Data Strategy. + */ + DataStrategyInterface *swapDataStrategy(DataStrategyInterface *dataStrategy); /** * @brief Returns the recipe that this widget is based on. This is optional, currently serves as a way to pass @@ -125,6 +123,45 @@ class SCOPY_IIO_WIDGETS_EXPORT IIOWidget : public QWidget */ int lastReturnCode(); + /** + * @brief setConfigurable A configurable widget will have a wheel button on the right and it will allow the + * use to modify the underlying data strategy at runtime. + * @param isConfigurable If true, the widget is configurable at runtime. The default is false. + */ + void setConfigurable(bool isConfigurable); + + /** + * @brief optionalData Calls the optionalData function from the data strategy. + * @return QString representing the optional data. + */ + QString optionalData() const; + + /** + * @brief data Calls the data functipm from the data strategy. + * @return QString represrnting the data. + */ + QString data() const; + + /** + * @brief isDSInstanceOf Checks whether the current data strategy is an instance of the specified type. + * @return True if the type specified coincides with the type of the data strategy. + */ + template + bool isDSInstanceOf() const + { + return dynamic_cast(m_dataStrategy) != nullptr; + } + + /** + * @brief isUISInstanceO Checks wheter the current UI strategy is an instance of the specified type. + * @return True if the type specified coincides with the type of the UI strategy. + */ + template + bool isUISInstanceOf() const + { + return dynamic_cast(m_uiStrategy) != nullptr; + } + Q_SIGNALS: /** * @brief Emits the current state of the IIOWidget system and a string containing a more @@ -132,6 +169,25 @@ class SCOPY_IIO_WIDGETS_EXPORT IIOWidget : public QWidget * */ void currentStateChanged(State currentState, QString explanation = ""); + /** + * @brief sendData Forwards the sendData signal from the Data Strategy (emits the newly read data). + * @param data The data read. + * @param dataOptions The data from the optional attribute read. + */ + void sendData(QString data, QString dataOptions); + + /** + * @brief emitStatus Forwarded signal from the Data Strategy for emitting the status of + * the operation that was just performed. + * @param timestamp QDateTime that holds the timestamp + * @param oldData The data that is present in the iio-widget before the operation + * @param newData The new data that was given to the operation + * @param returnCode int representing the return code of that operation + * @param isReadOp Boolean value set to true if the operation is a read + * operation and false if it is a write operation. + */ + void emitStatus(QDateTime timestamp, QString oldData, QString newData, int returnCode, bool isReadOp); + protected Q_SLOTS: void saveData(QString data); void emitDataStatus(QDateTime timestamp, QString oldData, QString newData, int returnCode, bool isReadOp); @@ -141,19 +197,27 @@ protected Q_SLOTS: protected: void initialize(); + void reconfigure(); void setLastOperationTimestamp(QDateTime timestamp); void setLastOperationState(IIOWidget::State state); + // Core variables GuiStrategyInterface *m_uiStrategy; DataStrategyInterface *m_dataStrategy; IIOWidgetFactoryRecipe m_recipe; + // Logged data QString m_lastData; SmallProgressBar *m_progressBar; QDateTime *m_lastOpTimestamp; int m_lastReturnCode; IIOWidget::State *m_lastOpState; + + // Optional configuration + QPushButton *m_configBtn; + IIOConfigurationPopup *m_configPopup; + bool m_isConfigurable; }; } // namespace scopy diff --git a/iio-widgets/include/iio-widgets/iiowidgetbuilder.h b/iio-widgets/include/iio-widgets/iiowidgetbuilder.h index df84e12453..1ac82b3403 100644 --- a/iio-widgets/include/iio-widgets/iiowidgetbuilder.h +++ b/iio-widgets/include/iio-widgets/iiowidgetbuilder.h @@ -90,6 +90,17 @@ class SCOPY_IIO_WIDGETS_EXPORT IIOWidgetBuilder : public QObject */ IIOWidgetBuilder &compactMode(bool isCompact); + /** + * @brief configMode Sets the IIOWidget to be configurable. This way, a + * wheel button will be created next to the IIOWidget. Once pressed, + * this button will allow the user to select a new attribute and the + * new data strategy will use that. + * @param isConfigurable If set to true, the IIOWidget will be configurable. + * Default is false. + * @return + */ + IIOWidgetBuilder &configMode(bool isConfigurable); + /** * @brief Sets the context that will be used, if no iio_device or iio_channel * is set, the build functions will work with the context. @@ -161,6 +172,7 @@ class SCOPY_IIO_WIDGETS_EXPORT IIOWidgetBuilder : public QObject Connection *m_connection; bool m_isCompact; + bool m_isConfigurable; struct iio_context *m_context; struct iio_device *m_device; struct iio_channel *m_channel; diff --git a/iio-widgets/include/iio-widgets/iiowidgetselector.h b/iio-widgets/include/iio-widgets/iiowidgetselector.h new file mode 100644 index 0000000000..ea22d9092c --- /dev/null +++ b/iio-widgets/include/iio-widgets/iiowidgetselector.h @@ -0,0 +1,30 @@ +#ifndef IIOWIDGETSELECTOR_H +#define IIOWIDGETSELECTOR_H + +#include +#include +#include +#include +#include +#include + +namespace scopy { +class IIOWidgetSelector : public QFrame +{ + Q_OBJECT +public: + IIOWidgetSelector(IIOItem *root, QWidget *parent = nullptr); + ~IIOWidgetSelector(); + QTreeView *getTree() const; + QStandardItemModel *getModel() const; + +Q_SIGNALS: + void itemSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + +private: + QTreeView *m_treeView; + QStandardItemModel *m_model; +}; +} // namespace scopy + +#endif // IIOWIDGETSELECTOR_H diff --git a/iio-widgets/src/guistrategy/comboguistrategy.cpp b/iio-widgets/src/guistrategy/comboguistrategy.cpp index b266657ea7..bb1776bb7a 100644 --- a/iio-widgets/src/guistrategy/comboguistrategy.cpp +++ b/iio-widgets/src/guistrategy/comboguistrategy.cpp @@ -24,7 +24,8 @@ using namespace scopy; ComboAttrUi::ComboAttrUi(IIOWidgetFactoryRecipe recipe, bool isCompact, QWidget *parent) - : m_ui(new QWidget(nullptr)) + : QWidget(parent) + , m_ui(new QWidget(this)) , m_isCompact(isCompact) { m_recipe = recipe; @@ -33,26 +34,26 @@ ComboAttrUi::ComboAttrUi(IIOWidgetFactoryRecipe recipe, bool isCompact, QWidget m_ui->setLayout(new QHBoxLayout(m_ui)); m_ui->layout()->setContentsMargins(0, 0, 0, 0); - auto label = new QLabel(recipe.data, m_ui); - StyleHelper::IIOCompactLabel(label, "IIOTitleLabel"); + m_compactLabel = new QLabel(recipe.data, m_ui); + StyleHelper::IIOCompactLabel(m_compactLabel, "IIOTitleLabel"); m_comboWidget = new QComboBox(m_ui); m_comboWidget->setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy::AdjustToContents); StyleHelper::IIOComboBox(m_comboWidget, "IIOComboBox"); m_comboWidget->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); - m_ui->layout()->addWidget(label); + m_ui->layout()->addWidget(m_compactLabel); m_ui->layout()->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Preferred)); m_ui->layout()->addWidget(m_comboWidget); } else { m_ui->setLayout(new QVBoxLayout(m_ui)); m_ui->layout()->setContentsMargins(0, 0, 0, 0); - auto comboMenuWidget = new MenuCombo(recipe.data, m_ui); - m_comboWidget = comboMenuWidget->combo(); + m_menuCombo = new MenuCombo(recipe.data, m_ui); + m_comboWidget = m_menuCombo->combo(); StyleHelper::IIOComboBox(m_comboWidget, "IIOComboBox"); - m_ui->layout()->addWidget(comboMenuWidget); + m_ui->layout()->addWidget(m_menuCombo); } connect(m_comboWidget, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) { @@ -63,7 +64,7 @@ ComboAttrUi::ComboAttrUi(IIOWidgetFactoryRecipe recipe, bool isCompact, QWidget Q_EMIT requestData(); } -ComboAttrUi::~ComboAttrUi() { m_ui->deleteLater(); } +ComboAttrUi::~ComboAttrUi() {} QWidget *ComboAttrUi::ui() { return m_ui; } @@ -96,4 +97,13 @@ void ComboAttrUi::receiveData(QString currentData, QString optionalData) Q_EMIT displayedNewData(currentData, optionalData); } +void ComboAttrUi::changeName(QString name) +{ + if(m_isCompact) { + m_compactLabel->setText(name); + } else { + m_menuCombo->label()->setText(name); + } +} + #include "moc_comboguistrategy.cpp" diff --git a/iio-widgets/src/guistrategy/editableguistrategy.cpp b/iio-widgets/src/guistrategy/editableguistrategy.cpp index d73cf4a535..bfb2e686c8 100644 --- a/iio-widgets/src/guistrategy/editableguistrategy.cpp +++ b/iio-widgets/src/guistrategy/editableguistrategy.cpp @@ -24,24 +24,24 @@ using namespace scopy; EditableGuiStrategy::EditableGuiStrategy(IIOWidgetFactoryRecipe recipe, bool isCompact, QWidget *parent) : QWidget(parent) - , m_ui(new QWidget(nullptr)) + , m_ui(new QWidget(this)) , m_lineEdit(new MenuLineEdit(m_ui)) { m_recipe = recipe; - QLabel *label = new QLabel(recipe.data, m_ui); + m_titleLabel = new QLabel(recipe.data, m_ui); if(isCompact) { m_ui->setLayout(new QHBoxLayout(m_ui)); - StyleHelper::IIOCompactLabel(label, "TitleLabel"); + StyleHelper::IIOCompactLabel(m_titleLabel, "TitleLabel"); m_lineEdit->edit()->setAlignment(Qt::AlignRight); } else { m_ui->setLayout(new QVBoxLayout(m_ui)); - StyleHelper::MenuSmallLabel(label, "MenuSmallLabel"); + StyleHelper::MenuSmallLabel(m_titleLabel, "MenuSmallLabel"); } StyleHelper::IIOLineEdit(m_lineEdit->edit(), "IIOLineEdit"); m_ui->layout()->setContentsMargins(0, 0, 0, 0); - m_ui->layout()->addWidget(label); + m_ui->layout()->addWidget(m_titleLabel); m_ui->layout()->addWidget(m_lineEdit); connect(m_lineEdit->edit(), &QLineEdit::editingFinished, this, [this]() { @@ -55,7 +55,7 @@ EditableGuiStrategy::EditableGuiStrategy(IIOWidgetFactoryRecipe recipe, bool isC Q_EMIT requestData(); } -EditableGuiStrategy::~EditableGuiStrategy() { m_ui->deleteLater(); } +EditableGuiStrategy::~EditableGuiStrategy() {} QWidget *EditableGuiStrategy::ui() { return m_ui; } @@ -75,4 +75,6 @@ void EditableGuiStrategy::receiveData(QString currentData, QString optionalData) Q_EMIT displayedNewData(currentData, optionalData); } +void EditableGuiStrategy::changeName(QString name) { m_titleLabel->setText(name); } + #include "moc_editableguistrategy.cpp" diff --git a/iio-widgets/src/guistrategy/rangeguistrategy.cpp b/iio-widgets/src/guistrategy/rangeguistrategy.cpp index b1e618a169..cacaa4bbdf 100644 --- a/iio-widgets/src/guistrategy/rangeguistrategy.cpp +++ b/iio-widgets/src/guistrategy/rangeguistrategy.cpp @@ -27,7 +27,7 @@ Q_LOGGING_CATEGORY(CAT_ATTR_GUI_STRATEGY, "AttrGuiStrategy") RangeAttrUi::RangeAttrUi(IIOWidgetFactoryRecipe recipe, bool isCompact, QWidget *parent) : QWidget(parent) - , m_ui(new QWidget(nullptr)) + , m_ui(new QWidget(this)) { m_recipe = recipe; if(!isValid()) { @@ -47,7 +47,7 @@ RangeAttrUi::RangeAttrUi(IIOWidgetFactoryRecipe recipe, bool isCompact, QWidget Q_EMIT requestData(); } -RangeAttrUi::~RangeAttrUi() { m_ui->deleteLater(); } +RangeAttrUi::~RangeAttrUi() {} QWidget *RangeAttrUi::ui() { return m_ui; } @@ -122,6 +122,8 @@ void RangeAttrUi::receiveData(QString currentData, QString optionalData) Q_EMIT displayedNewData(currentData, optionalData); } +void RangeAttrUi::changeName(QString name) { m_spinBox->setTitle(name); } + double RangeAttrUi::tryParse(QString number, bool *success) { // Try to parse as double first diff --git a/iio-widgets/src/guistrategy/switchguistrategy.cpp b/iio-widgets/src/guistrategy/switchguistrategy.cpp index 22f6b7e44d..9a22a40de5 100644 --- a/iio-widgets/src/guistrategy/switchguistrategy.cpp +++ b/iio-widgets/src/guistrategy/switchguistrategy.cpp @@ -27,7 +27,7 @@ Q_LOGGING_CATEGORY(CAT_SWITCHGUISTRATEGY, "SwitchGuiStrategy") SwitchAttrUi::SwitchAttrUi(IIOWidgetFactoryRecipe recipe, bool isCompact, QWidget *parent) : QWidget(parent) - , m_ui(new QWidget(nullptr)) + , m_ui(new QWidget(this)) , m_optionsList(new QStringList) { m_recipe = recipe; @@ -82,4 +82,6 @@ void SwitchAttrUi::receiveData(QString currentData, QString optionalData) Q_EMIT displayedNewData(currentData, optionalData); } +void SwitchAttrUi::changeName(QString name) { m_menuBigSwitch->setText(name); } + #include "moc_switchguistrategy.cpp" diff --git a/iio-widgets/src/iioconfigurationpopup.cpp b/iio-widgets/src/iioconfigurationpopup.cpp new file mode 100644 index 0000000000..68ea66a3f9 --- /dev/null +++ b/iio-widgets/src/iioconfigurationpopup.cpp @@ -0,0 +1,184 @@ +#include "iioconfigurationpopup.h" + +#include +#include +#include +#include + +using namespace scopy; + +Q_LOGGING_CATEGORY(CAT_IIOCONFIGURATIONPOPUP, "IIOConfigurationPopup") + +IIOConfigurationPopup::IIOConfigurationPopup(iio_context *ctx, QWidget *parent) + : QWidget{parent} + , m_tintedOverlay(nullptr) + , m_widgetSelector(nullptr) +{ + Connection *conn = ConnectionProvider::open(ctx); + IIOTreeScan *scan = conn->iioTreeScan(); + m_root = scan->getRoot(); + m_widgetSelector = new IIOWidgetSelector(m_root, this); + connect(m_widgetSelector, &IIOWidgetSelector::itemSelectionChanged, this, + &IIOConfigurationPopup::modelSelectionChanged); + m_widgetSelector->hide(); + + init(); +} + +IIOConfigurationPopup::IIOConfigurationPopup(iio_device *dev, QWidget *parent) + : QWidget{parent} + , m_tintedOverlay(nullptr) + , m_widgetSelector(nullptr) +{ + const iio_context *ctx = iio_device_get_context(dev); + Connection *conn = ConnectionProvider::open(const_cast(ctx)); + IIOTreeScan *scan = conn->iioTreeScan(); + m_root = scan->getRoot(); + m_widgetSelector = new IIOWidgetSelector(m_root, this); + connect(m_widgetSelector, &IIOWidgetSelector::itemSelectionChanged, this, + &IIOConfigurationPopup::modelSelectionChanged); + m_widgetSelector->hide(); + + init(); +} + +IIOConfigurationPopup::IIOConfigurationPopup(iio_channel *chnl, QWidget *parent) + : QWidget{parent} + , m_tintedOverlay(nullptr) + , m_widgetSelector(nullptr) +{ + const iio_device *dev = iio_channel_get_device(chnl); + const iio_context *ctx = iio_device_get_context(dev); + Connection *conn = ConnectionProvider::open(const_cast(ctx)); + IIOTreeScan *scan = conn->iioTreeScan(); + m_root = scan->getRoot(); + m_widgetSelector = new IIOWidgetSelector(m_root, this); + connect(m_widgetSelector, &IIOWidgetSelector::itemSelectionChanged, this, + &IIOConfigurationPopup::modelSelectionChanged); + m_widgetSelector->hide(); + + init(); +} + +IIOConfigurationPopup::~IIOConfigurationPopup() { delete m_tintedOverlay; } + +void IIOConfigurationPopup::enableTintedOverlay(bool enable) +{ + if(enable) { + delete m_tintedOverlay; + + m_widgetSelector->show(); + m_tintedOverlay = new gui::TintedOverlay(parentWidget()); + m_tintedOverlay->show(); + raise(); + show(); + move(parentWidget()->rect().center() - rect().center()); + } else { + delete m_tintedOverlay; + m_tintedOverlay = nullptr; + } +} + +void IIOConfigurationPopup::modelSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ + Q_UNUSED(deselected) + + if(selected.indexes().isEmpty()) { + qWarning(CAT_IIOCONFIGURATIONPOPUP) << "Selected index cannot be found."; + return; + } + + // There will be only one selected + QModelIndex index = selected.indexes().first(); + QStandardItem *item = m_widgetSelector->getModel()->itemFromIndex(index); + + IIOItem *iioItem = dynamic_cast(item); + if(!iioItem) { + qWarning(CAT_IIOCONFIGURATIONPOPUP) << "Received item is not an IIOItem."; + return; + } + + switch(iioItem->type()) { + case IIOItem::CHANNEL_ATTR: + case IIOItem::DEVICE_ATTR: + case IIOItem::CONTEXT_ATTR: + m_selectButton->setEnabled(true); + break; + default: + m_selectButton->setEnabled(false); + break; + } +} + +void IIOConfigurationPopup::init() +{ + initUI(); + QObject::connect(m_selectButton, &QPushButton::clicked, this, [&] { + const QModelIndex currentIndex = m_widgetSelector->getTree()->selectionModel()->currentIndex(); + if(!currentIndex.isValid()) { + qWarning(CAT_IIOCONFIGURATIONPOPUP) << "Current selection index is not valid."; + return; + } + + QStandardItem *item = m_widgetSelector->getModel()->itemFromIndex(currentIndex); + if(!item) { + qWarning(CAT_IIOCONFIGURATIONPOPUP) << "Cannot get current item from index."; + return; + } + + IIOItem *iioItem = dynamic_cast(item); + if(!iioItem) { + qWarning(CAT_IIOCONFIGURATIONPOPUP) << "Cannot cast QStandardItem to IIOItem."; + return; + } + + Q_EMIT selectButtonClicked(iioItem); + }); + QObject::connect(m_exitButton, &QPushButton::clicked, this, &IIOConfigurationPopup::exitButtonClicked); + m_selectButton->setDisabled(true); +} + +void IIOConfigurationPopup::initUI() +{ + this->setObjectName("PopupWidget"); + this->setStyleSheet(""); + this->resize(500, 300); + auto verticalLayout = new QVBoxLayout(this); + verticalLayout->setContentsMargins(0, 0, 0, 0); + this->setLayout(verticalLayout); + + auto backgroundWidget = new QWidget(this); + auto backgroundLayout = new QVBoxLayout(backgroundWidget); + verticalLayout->addWidget(backgroundWidget); + + m_titleLabel = new QLabel(backgroundWidget); + m_titleLabel->setObjectName("titleLabel"); + m_titleLabel->setText("Select a replacement attribute."); + + auto buttonGroup = new QWidget(backgroundWidget); + auto buttonGroupLayout = new QHBoxLayout(buttonGroup); + buttonGroupLayout->setContentsMargins(0, 0, 0, 0); + buttonGroupLayout->setSpacing(10); + + m_selectButton = new QPushButton("Select", buttonGroup); + m_selectButton->setObjectName("selectButton"); + + m_exitButton = new QPushButton("Exit", buttonGroup); + m_exitButton->setObjectName("exitButton"); + + buttonGroupLayout->addWidget(m_exitButton); + buttonGroupLayout->addWidget(m_selectButton); + + backgroundLayout->addWidget(m_titleLabel); + backgroundLayout->addWidget(m_widgetSelector); + backgroundLayout->addWidget(buttonGroup); + + backgroundWidget->setLayout(backgroundLayout); + + StyleHelper::TutorialChapterTitleLabel(m_titleLabel, "titleLabel"); + StyleHelper::BlueButton(m_selectButton, "selectButton"); + StyleHelper::BlueButton(m_exitButton, "exitButton"); + StyleHelper::OverlayMenu(this, "IIOConfigurationPopupOverlay"); +} + +#include "moc_iioconfigurationpopup.cpp" diff --git a/iio-widgets/src/iiowidget.cpp b/iio-widgets/src/iiowidget.cpp index f594fd9376..ed579bc853 100644 --- a/iio-widgets/src/iiowidget.cpp +++ b/iio-widgets/src/iiowidget.cpp @@ -19,8 +19,15 @@ */ #include "iiowidget.h" +#include "channelattrdatastrategy.h" +#include "contextattrdatastrategy.h" +#include "deviceattrdatastrategy.h" +#include "iiowidgetselector.h" +#include #include +#include #include +#include using namespace scopy; @@ -31,9 +38,11 @@ IIOWidget::IIOWidget(GuiStrategyInterface *uiStrategy, DataStrategyInterface *da , m_uiStrategy(uiStrategy) , m_dataStrategy(dataStrategy) , m_progressBar(new SmallProgressBar(this)) + , m_configBtn(new QPushButton(this)) , m_lastOpTimestamp(nullptr) , m_lastOpState(nullptr) , m_lastReturnCode(0) + , m_isConfigurable(false) { bool useLazyLoading = Preferences::GetInstance()->get("iiowidgets_use_lazy_loading").toBool(); if(!useLazyLoading) { // force skip lazy load @@ -49,6 +58,41 @@ void IIOWidget::readAsync() { m_dataStrategy->readAsync(); } void IIOWidget::writeAsync(QString data) { m_dataStrategy->writeAsync(data); } +DataStrategyInterface *IIOWidget::swapDataStrategy(DataStrategyInterface *dataStrategy) +{ + QWidget *dataStrategyWidget = dynamic_cast(m_dataStrategy); + QWidget *uiStrategyWidget = dynamic_cast(m_uiStrategy); + QWidget *newDataStrategyWidget = dynamic_cast(dataStrategy); + + // disconnect old data strategy + disconnect(dataStrategyWidget, SIGNAL(emitStatus(QDateTime, QString, QString, int, bool)), this, + SLOT(emitDataStatus(QDateTime, QString, QString, int, bool))); + disconnect(uiStrategyWidget, SIGNAL(requestData()), dataStrategyWidget, SLOT(readAsync())); + disconnect(dataStrategyWidget, SIGNAL(sendData(QString, QString)), uiStrategyWidget, + SLOT(receiveData(QString, QString))); + disconnect(dataStrategyWidget, SIGNAL(sendData(QString, QString)), this, + SLOT(storeReadInfo(QString, QString))); // TODO: maybe do something with this slot.. + disconnect(dataStrategyWidget, SIGNAL(sendData(QString, QString)), this, SIGNAL(sendData(QString, QString))); + disconnect(dataStrategyWidget, SIGNAL(emitStatus(QDateTime, QString, QString, int, bool)), this, + SIGNAL(emitStatus(QDateTime, QString, QString, int, bool))); + + // connect new data strategy + connect(newDataStrategyWidget, SIGNAL(emitStatus(QDateTime, QString, QString, int, bool)), this, + SLOT(emitDataStatus(QDateTime, QString, QString, int, bool))); + connect(uiStrategyWidget, SIGNAL(requestData()), newDataStrategyWidget, SLOT(readAsync())); + connect(newDataStrategyWidget, SIGNAL(sendData(QString, QString)), uiStrategyWidget, + SLOT(receiveData(QString, QString))); + connect(newDataStrategyWidget, SIGNAL(sendData(QString, QString)), this, SLOT(storeReadInfo(QString, QString))); + connect(dataStrategyWidget, SIGNAL(sendData(QString, QString)), this, SIGNAL(sendData(QString, QString))); + connect(dataStrategyWidget, SIGNAL(emitStatus(QDateTime, QString, QString, int, bool)), this, + SIGNAL(emitStatus(QDateTime, QString, QString, int, bool))); + + // save the new data strategy and return the old one + DataStrategyInterface *oldDS = m_dataStrategy; + m_dataStrategy = dataStrategy; + return oldDS; +} + void IIOWidget::saveData(QString data) { setLastOperationState(IIOWidget::Busy); @@ -96,10 +140,6 @@ void IIOWidget::emitDataStatus(QDateTime timestamp, QString oldData, QString new timer->start(4000); } -GuiStrategyInterface *IIOWidget::getUiStrategy() { return m_uiStrategy; } - -DataStrategyInterface *IIOWidget::getDataStrategy() { return m_dataStrategy; } - IIOWidgetFactoryRecipe IIOWidget::getRecipe() { return m_recipe; } void IIOWidget::setRecipe(IIOWidgetFactoryRecipe recipe) { m_recipe = recipe; } @@ -110,6 +150,16 @@ IIOWidget::State *IIOWidget::lastOperationState() { return m_lastOpState; } int IIOWidget::lastReturnCode() { return m_lastReturnCode; } +void IIOWidget::setConfigurable(bool isConfigurable) +{ + m_isConfigurable = isConfigurable; + m_configBtn->setVisible(m_isConfigurable); +} + +QString IIOWidget::optionalData() const { return m_dataStrategy->optionalData(); } + +QString IIOWidget::data() const { return m_dataStrategy->data(); } + void IIOWidget::startTimer(QString data) { m_lastData = data; @@ -127,20 +177,33 @@ void IIOWidget::storeReadInfo(QString data, QString optionalData) void IIOWidget::initialize() { + // Config button + m_configBtn->setStyleSheet("border-image: url(\":/gui/icons/scopy-default/icons/gear_wheel.svg\");"); + m_configBtn->setVisible(m_isConfigurable); + + // General layout setLayout(new QVBoxLayout(this)); layout()->setContentsMargins(0, 0, 0, 0); layout()->setSpacing(0); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + QWidget *topWidget = new QWidget(this); + topWidget->setLayout(new QHBoxLayout(topWidget)); + topWidget->layout()->setContentsMargins(0, 0, 0, 0); + QWidget *ui = m_uiStrategy->ui(); if(ui) { - layout()->addWidget(ui); + topWidget->layout()->addWidget(ui); } + topWidget->layout()->addWidget(m_configBtn); + + layout()->addWidget(topWidget); layout()->addWidget(m_progressBar); QWidget *uiStrategyWidget = dynamic_cast(m_uiStrategy); QWidget *dataStrategyWidget = dynamic_cast(m_dataStrategy); + connect(m_configBtn, &QPushButton::clicked, this, &IIOWidget::reconfigure); connect(m_progressBar, &SmallProgressBar::progressFinished, this, [this]() { this->saveData(m_lastData); }); connect(uiStrategyWidget, SIGNAL(emitData(QString)), this, SLOT(startTimer(QString))); @@ -150,6 +213,9 @@ void IIOWidget::initialize() // forward data request from ui strategy to data strategy connect(uiStrategyWidget, SIGNAL(requestData()), dataStrategyWidget, SLOT(readAsync())); + connect(dataStrategyWidget, SIGNAL(sendData(QString, QString)), this, SIGNAL(sendData(QString, QString))); + connect(dataStrategyWidget, SIGNAL(emitStatus(QDateTime, QString, QString, int, bool)), this, + SIGNAL(emitStatus(QDateTime, QString, QString, int, bool))); // forward data from data strategy to ui strategy connect(dataStrategyWidget, SIGNAL(sendData(QString, QString)), uiStrategyWidget, @@ -158,12 +224,53 @@ void IIOWidget::initialize() // intercept the sendData from dataStrategy to collect information connect(dataStrategyWidget, SIGNAL(sendData(QString, QString)), this, SLOT(storeReadInfo(QString, QString))); - connect(dynamic_cast(m_dataStrategy), SIGNAL(sendData(QString, QString)), this, - SLOT(storeReadInfo(QString, QString))); - m_dataStrategy->readAsync(); } +void IIOWidget::reconfigure() +{ + // display the popup and switch the DS + if(m_recipe.context) { + m_configPopup = new IIOConfigurationPopup(m_recipe.context, qApp->activeWindow()); + } else if(m_recipe.device) { + m_configPopup = new IIOConfigurationPopup(m_recipe.device, qApp->activeWindow()); + } else if(m_recipe.channel) { + m_configPopup = new IIOConfigurationPopup(m_recipe.channel, qApp->activeWindow()); + } else { + qCritical(CAT_IIOWIDGET) << "No available context/device/channel"; + } + + connect(m_configPopup, &IIOConfigurationPopup::exitButtonClicked, this, + [&]() { m_configPopup->deleteLater(); }); + connect(m_configPopup, &IIOConfigurationPopup::selectButtonClicked, this, [&](IIOItem *item) { + DataStrategyInterface *dsCreated = nullptr; + switch(item->type()) { + case IIOItem::CHANNEL_ATTR: + dsCreated = new ChannelAttrDataStrategy({.channel = item->chnl(), .data = item->name()}, this); + break; + case IIOItem::DEVICE_ATTR: + dsCreated = new DeviceAttrDataStrategy({.device = item->dev(), .data = item->name()}, this); + break; + case IIOItem::CONTEXT_ATTR: + dsCreated = new ContextAttrDataStrategy({.context = item->ctx(), .data = item->name()}, this); + default: + break; + } + + if(!dsCreated) { + qWarning(CAT_IIOWIDGET) << "Could not create a new data strategy."; + return; + } + + m_uiStrategy->changeName(item->name().toUpper()); + DataStrategyInterface *oldDS = swapDataStrategy(dsCreated); + dsCreated->readAsync(); + delete oldDS; + delete m_configPopup; + }); + m_configPopup->enableTintedOverlay(true); +} + void IIOWidget::setLastOperationTimestamp(QDateTime timestamp) { if(m_lastOpTimestamp == nullptr) { diff --git a/iio-widgets/src/iiowidgetbuilder.cpp b/iio-widgets/src/iiowidgetbuilder.cpp index 74aa37612f..6f0aeea34b 100644 --- a/iio-widgets/src/iiowidgetbuilder.cpp +++ b/iio-widgets/src/iiowidgetbuilder.cpp @@ -40,6 +40,7 @@ IIOWidgetBuilder::IIOWidgetBuilder(QObject *parent) : QObject(parent) , m_connection(nullptr) , m_isCompact(false) + , m_isConfigurable(false) , m_context(nullptr) , m_device(nullptr) , m_channel(nullptr) @@ -83,6 +84,7 @@ IIOWidget *IIOWidgetBuilder::buildSingle() IIOWidget *widget = new IIOWidget(ui, ds, m_widgetParent); widget->setRecipe(m_generatedRecipe); + widget->setConfigurable(m_isConfigurable); return widget; } @@ -176,6 +178,7 @@ void IIOWidgetBuilder::clear() { m_connection = nullptr; m_isCompact = false; + m_isConfigurable = false; m_context = nullptr; m_device = nullptr; m_channel = nullptr; @@ -199,6 +202,12 @@ IIOWidgetBuilder &IIOWidgetBuilder::compactMode(bool isCompact) return *this; } +IIOWidgetBuilder &IIOWidgetBuilder::configMode(bool isConfigurable) +{ + m_isConfigurable = isConfigurable; + return *this; +} + IIOWidgetBuilder &IIOWidgetBuilder::context(iio_context *context) { m_context = context; diff --git a/iio-widgets/src/iiowidgetselector.cpp b/iio-widgets/src/iiowidgetselector.cpp new file mode 100644 index 0000000000..25451f71df --- /dev/null +++ b/iio-widgets/src/iiowidgetselector.cpp @@ -0,0 +1,37 @@ +#include "iiowidgetselector.h" + +#include +#include + +using namespace scopy; + +IIOWidgetSelector::IIOWidgetSelector(IIOItem *root, QWidget *parent) + : QFrame(parent) +{ + setLayout(new QVBoxLayout(this)); + layout()->setContentsMargins(0, 0, 0, 0); + m_treeView = new QTreeView(this); + m_model = new QStandardItemModel(m_treeView); + m_model->appendRow(root); + m_treeView->setModel(m_model); + m_treeView->expand(m_model->index(0, 0)); + m_treeView->setHeaderHidden(true); + layout()->addWidget(m_treeView); + StyleHelper::TreeViewDebugger(m_treeView, "TreeView"); + setStyleSheet("background-color: " + StyleHelper::getColor("ScopyBackground")); + + connect(m_treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, + &IIOWidgetSelector::itemSelectionChanged); +} + +IIOWidgetSelector::~IIOWidgetSelector() +{ + // This should be done so that the root item is not deleted when the model is deleted + m_model->takeRow(0); +} + +QTreeView *IIOWidgetSelector::getTree() const { return m_treeView; } + +QStandardItemModel *IIOWidgetSelector::getModel() const { return m_model; } + +#include "moc_iiowidgetselector.cpp" diff --git a/iioutil/include/iioutil/connection.h b/iioutil/include/iioutil/connection.h index c6d8688167..e7e6dbee37 100644 --- a/iioutil/include/iioutil/connection.h +++ b/iioutil/include/iioutil/connection.h @@ -2,6 +2,7 @@ #define CONNECTION_H #include "scopy-iioutil_export.h" #include "commandqueue.h" +#include "iiotreescan.h" #include #include @@ -14,6 +15,7 @@ class SCOPY_IIOUTIL_EXPORT Connection : public QObject const QString &uri() const; CommandQueue *commandQueue() const; + IIOTreeScan *iioTreeScan() const; struct iio_context *context() const; int refCount() const; @@ -57,6 +59,7 @@ class SCOPY_IIOUTIL_EXPORT Connection : public QObject friend class ConnectionProvider; QString m_uri; CommandQueue *m_commandQueue; + IIOTreeScan *m_iioTreeScan; struct iio_context *m_context; int m_refCount = 0; }; diff --git a/iioutil/include/iioutil/iioitem.h b/iioutil/include/iioutil/iioitem.h new file mode 100644 index 0000000000..fe7b637fe5 --- /dev/null +++ b/iioutil/include/iioutil/iioitem.h @@ -0,0 +1,76 @@ +#ifndef IIOITEM_H +#define IIOITEM_H + +#include "scopy-iioutil_export.h" +#include +#include +#include + +namespace scopy { +class SCOPY_IIOUTIL_EXPORT IIOItem : public QStandardItem +{ +public: + // User defined types should be > 1000, the rest are Qt reserved + // In order to ensure that the user cannot enter a type and a + // different iio struct, we will separate the types in 3 enums + enum IIOTypeCtx + { + CONTEXT = 1001, + CONTEXT_ATTR = 1002, + }; + + enum IIOTypeDev + { + DEVICE = 1003, + DEVICE_ATTR = 1004, + }; + + enum IIOTypeChnl + { + CHANNEL = 1005, + CHANNEL_ATTR = 1006, + }; + + IIOItem(QString name, QString id, IIOTypeCtx type, iio_context *ctx, IIOItem *parent = nullptr); + IIOItem(QString name, QString id, IIOTypeDev type, iio_device *dev, IIOItem *parent = nullptr); + IIOItem(QString name, QString id, IIOTypeChnl type, iio_channel *chnl, IIOItem *parent = nullptr); + virtual ~IIOItem(); + int type() const override; + + // Navigation functions + int childCount() const; + void addChild(IIOItem *item); + void removeChild(IIOItem *item); + IIOItem *child(int index) const; + IIOItem *child(QString name) const; + IIOItem *parent() const; + void setParent(IIOItem *parent); + + // Data functions + QString name() const; + QString id() const; + + iio_context *ctx() const; + void setCtx(iio_context *newCtx); + + iio_device *dev() const; + void setDev(iio_device *newDev); + + iio_channel *chnl() const; + void setChnl(iio_channel *newChnl); + +private: + int m_type; + IIOItem *m_parent; + QList m_children; + + struct iio_context *m_ctx; + struct iio_device *m_dev; + struct iio_channel *m_chnl; + + QString m_name; + QString m_id; +}; +} // namespace scopy + +#endif // IIOITEM_H diff --git a/iioutil/include/iioutil/iiotreescan.h b/iioutil/include/iioutil/iiotreescan.h new file mode 100644 index 0000000000..1dc81b4537 --- /dev/null +++ b/iioutil/include/iioutil/iiotreescan.h @@ -0,0 +1,25 @@ +#ifndef IIOTREESCAN_H +#define IIOTREESCAN_H + +#include +#include "iioitem.h" +#include +#include "scopy-iioutil_export.h" + +#include + +namespace scopy { +class SCOPY_IIOUTIL_EXPORT IIOTreeScan : public QObject +{ + Q_OBJECT +public: + explicit IIOTreeScan(struct iio_context *ctx, QObject *parent = nullptr); + ~IIOTreeScan(); + IIOItem *getRoot(); + +private: + IIOItem *m_rootItem; +}; +} // namespace scopy + +#endif // IIOTREESCAN_H diff --git a/iioutil/src/connection.cpp b/iioutil/src/connection.cpp index 7c12757631..5022bb007b 100644 --- a/iioutil/src/connection.cpp +++ b/iioutil/src/connection.cpp @@ -7,6 +7,7 @@ Connection::Connection(QString uri) this->m_uri = uri; this->m_context = nullptr; this->m_commandQueue = nullptr; + this->m_iioTreeScan = nullptr; this->m_refCount = 0; } @@ -16,6 +17,10 @@ Connection::~Connection() delete this->m_commandQueue; this->m_commandQueue = nullptr; } + if(this->m_iioTreeScan) { + delete this->m_iioTreeScan; + this->m_iioTreeScan = nullptr; + } if(this->m_context) { iio_context_destroy(this->m_context); this->m_context = nullptr; @@ -26,6 +31,8 @@ const QString &Connection::uri() const { return m_uri; } CommandQueue *Connection::commandQueue() const { return m_commandQueue; } +IIOTreeScan *Connection::iioTreeScan() const { return m_iioTreeScan; } + iio_context *Connection::context() const { return m_context; } int Connection::refCount() const { return m_refCount; } @@ -36,6 +43,7 @@ void Connection::open() this->m_context = iio_create_context_from_uri(this->m_uri.toStdString().c_str()); if(this->m_context) { this->m_commandQueue = new CommandQueue(); + this->m_iioTreeScan = new IIOTreeScan(this->m_context); this->m_refCount++; } } else { diff --git a/iioutil/src/iioitem.cpp b/iioutil/src/iioitem.cpp new file mode 100644 index 0000000000..ad08de3809 --- /dev/null +++ b/iioutil/src/iioitem.cpp @@ -0,0 +1,93 @@ +#include "iioitem.h" + +using namespace scopy; + +IIOItem::IIOItem(QString name, QString id, IIOTypeCtx type, iio_context *ctx, IIOItem *parent) + : QStandardItem((id.isEmpty()) ? name : id + ((name.isEmpty()) ? " " : ": " + name)) + , m_name(name) + , m_id(id) + , m_type(type) + , m_ctx(ctx) + , m_parent(parent) +{} + +IIOItem::IIOItem(QString name, QString id, IIOTypeDev type, iio_device *dev, IIOItem *parent) + : QStandardItem((id.isEmpty()) ? name : id + ((name.isEmpty()) ? " " : ": " + name)) + , m_name(name) + , m_id(id) + , m_type(type) + , m_dev(dev) + , m_parent(parent) +{} + +IIOItem::IIOItem(QString name, QString id, IIOTypeChnl type, iio_channel *chnl, IIOItem *parent) + : QStandardItem((id.isEmpty()) ? name : id + ((name.isEmpty()) ? " " : ": " + name)) + , m_name(name) + , m_id(id) + , m_type(type) + , m_chnl(chnl) + , m_parent(parent) +{} + +IIOItem::~IIOItem() {} + +int IIOItem::type() const { return m_type; } + +int IIOItem::childCount() const { return m_children.size(); } + +void IIOItem::addChild(IIOItem *item) +{ + item->setParent(this); + appendRow(item); + m_children.append(item); // Maintain out own list for easy access +} + +void IIOItem::removeChild(IIOItem *item) +{ + int row = m_children.indexOf(item); + if(row >= 0) { + removeRow(row); + m_children.removeAt(row); + delete item; + } +} + +IIOItem *IIOItem::child(int index) const +{ + if(index >= 0 && index < m_children.size()) { + return m_children.at(index); + } + + return nullptr; +} + +IIOItem *IIOItem::child(QString name) const +{ + for(int i = 0; i < m_children.size(); ++i) { + if(m_children.at(i)->name() == name) { + return m_children.at(i); + } + } + + return nullptr; +} + +IIOItem *IIOItem::parent() const { return m_parent; } + +void IIOItem::setParent(IIOItem *parent) { m_parent = parent; } + +QString IIOItem::name() const { return m_name; } + +QString IIOItem::id() const { return m_id; } + +iio_context *IIOItem::ctx() const { return m_ctx; } + +void IIOItem::setCtx(iio_context *newCtx) { m_ctx = newCtx; } + +iio_device *IIOItem::dev() const { return m_dev; } + +void IIOItem::setDev(iio_device *newDev) { m_dev = newDev; } + +iio_channel *IIOItem::chnl() const { return m_chnl; } + +void IIOItem::setChnl(iio_channel *newChnl) { m_chnl = newChnl; } diff --git a/iioutil/src/iiotreescan.cpp b/iioutil/src/iiotreescan.cpp new file mode 100644 index 0000000000..f7707b4ae7 --- /dev/null +++ b/iioutil/src/iiotreescan.cpp @@ -0,0 +1,72 @@ +#include "iiotreescan.h" +#include +#include +#include + +Q_LOGGING_CATEGORY(CAT_IIOTREESCAN, "IIOTreeScan") + +using namespace scopy; + +IIOTreeScan::IIOTreeScan(iio_context *ctx, QObject *parent) + : QObject{parent} +{ + m_rootItem = new IIOItem(iio_context_get_name(ctx), "", IIOItem::CONTEXT, ctx, nullptr); + m_rootItem->setEditable(false); + + int ctxAttrs = iio_context_get_attrs_count(ctx); + for(int i = 0; i < ctxAttrs; ++i) { + const char *ctxAttrName; + const char *ctxAttrValue; + int res = iio_context_get_attr(ctx, i, &ctxAttrName, &ctxAttrValue); + if(res < 0) { + qWarning(CAT_IIOTREESCAN) << "Error when reading the context attr" << i; + continue; + } + IIOItem *item = new IIOItem(ctxAttrName, "", IIOItem::CONTEXT_ATTR, ctx, m_rootItem); + item->setEditable(false); + m_rootItem->addChild(item); + } + + int ctxDevs = iio_context_get_devices_count(ctx); + for(int i = 0; i < ctxDevs; ++i) { + iio_device *dev = iio_context_get_device(ctx, i); + QString devName = iio_device_get_name(dev); + QString devId = iio_device_get_id(dev); + IIOItem *devItem = new IIOItem(devName, devId, IIOItem::DEVICE, dev, m_rootItem); + devItem->setEditable(false); + + int devAttrCount = iio_device_get_attrs_count(dev); + for(int j = 0; j < devAttrCount; ++j) { + QString devAttrName = iio_device_get_attr(dev, j); + IIOItem *devAttrItem = new IIOItem(devAttrName, "", IIOItem::DEVICE_ATTR, dev, devItem); + devAttrItem->setEditable(false); + devItem->addChild(devAttrItem); + } + + int devChannelCount = iio_device_get_channels_count(dev); + for(int j = 0; j < devChannelCount; ++j) { + iio_channel *chnl = iio_device_get_channel(dev, j); + QString channelName = iio_channel_get_name(chnl); + QString channelId = iio_channel_get_id(chnl); + IIOItem *chnlItem = new IIOItem(channelName, channelId, IIOItem::CHANNEL, chnl, devItem); + chnlItem->setEditable(false); + + int chnlAttrCount = iio_channel_get_attrs_count(chnl); + for(int k = 0; k < chnlAttrCount; ++k) { + QString channelAttrName = iio_channel_get_attr(chnl, k); + IIOItem *chnlAttrItem = + new IIOItem(channelAttrName, "", IIOItem::CHANNEL_ATTR, chnl, chnlItem); + chnlAttrItem->setEditable(false); + chnlItem->addChild(chnlAttrItem); + } + devItem->addChild(chnlItem); + } + m_rootItem->addChild(devItem); + } +} + +IIOTreeScan::~IIOTreeScan() { qDebug(CAT_IIOTREESCAN) << "IIOTreeScan object dtor"; } + +IIOItem *IIOTreeScan::getRoot() { return m_rootItem; } + +#include "moc_iiotreescan.cpp" diff --git a/plugins/datalogger/src/menus/channelattributesmenu.cpp b/plugins/datalogger/src/menus/channelattributesmenu.cpp index e78c94bb0f..51cd5a28f5 100644 --- a/plugins/datalogger/src/menus/channelattributesmenu.cpp +++ b/plugins/datalogger/src/menus/channelattributesmenu.cpp @@ -39,7 +39,10 @@ ChannelAttributesMenu::ChannelAttributesMenu(DataMonitorModel *model, QWidget *p if(qobject_cast(model)) { QList attrWidgets = - IIOWidgetBuilder().channel(dynamic_cast(model)->iioChannel()).buildAll(); + IIOWidgetBuilder() + .channel(dynamic_cast(model)->iioChannel()) + .parent(parent) + .buildAll(); for(auto w : attrWidgets) { attrLayout->addWidget(w); diff --git a/plugins/debugger/src/iioexplorer/clidetailsview.cpp b/plugins/debugger/src/iioexplorer/clidetailsview.cpp index 9f560de4b7..078491e380 100644 --- a/plugins/debugger/src/iioexplorer/clidetailsview.cpp +++ b/plugins/debugger/src/iioexplorer/clidetailsview.cpp @@ -85,12 +85,11 @@ void CliDetailsView::setupUi() void CliDetailsView::setupChannelAttr() { IIOWidget *w = m_channelIIOItem->getIIOWidgets()[0]; - DataStrategyInterface *ds = w->getDataStrategy(); m_currentText.append(tabs(4) + "attr " + QString::number(m_noChnlAttributes) + ": " + m_channelIIOItem->name() + - " value: " + ds->data() + "\n"); + " value: " + w->data() + "\n"); ++m_noChnlAttributes; - QString channelOptData = ds->optionalData(); + QString channelOptData = w->optionalData(); if(!channelOptData.isEmpty()) { m_currentText.append(tabs(4) + "attr " + QString::number(m_noChnlAttributes) + ": " + w->getRecipe().iioDataOptions + " value: " + channelOptData + "\n"); @@ -126,13 +125,11 @@ void CliDetailsView::setupChannel() void CliDetailsView::setupDeviceAttr() { IIOWidget *w = m_deviceIIOItem->getIIOWidgets()[0]; - DataStrategyInterface *ds = w->getDataStrategy(); - m_deviceAttrsString.append(tabs(4) + "attr " + QString::number(m_noDevAttributes) + ": " + - m_deviceIIOItem->name() + " value: " + ds->data() + "\n"); + m_deviceIIOItem->name() + " value: " + w->data() + "\n"); ++m_noDevAttributes; - QString deviceOptData = ds->optionalData(); + QString deviceOptData = w->optionalData(); if(!deviceOptData.isEmpty()) { m_deviceAttrsString.append(tabs(4) + "attr " + QString::number(m_noDevAttributes) + ": " + w->getRecipe().iioDataOptions + " value: " + deviceOptData + "\n"); @@ -182,8 +179,8 @@ void CliDetailsView::setupDevice() void CliDetailsView::setupContextAttr() { ++m_noCtxAttributes; - m_currentText.append(tabs(1) + m_contextIIOItem->name() + ": " + - m_contextIIOItem->getIIOWidgets()[0]->getDataStrategy()->data() + "\n"); + m_currentText.append(tabs(1) + m_contextIIOItem->name() + ": " + m_contextIIOItem->getIIOWidgets()[0]->data() + + "\n"); } void CliDetailsView::setupContext() diff --git a/plugins/debugger/src/iioexplorer/iioexplorerinstrument.cpp b/plugins/debugger/src/iioexplorer/iioexplorerinstrument.cpp index a13543e7b0..9d7110d310 100644 --- a/plugins/debugger/src/iioexplorer/iioexplorerinstrument.cpp +++ b/plugins/debugger/src/iioexplorer/iioexplorerinstrument.cpp @@ -286,7 +286,7 @@ void IIOExplorerInstrument::triggerReadOnAllChildItems(QStandardItem *item) QList iioWidgets = IIOitem->getIIOWidgets(); for(int i = 0; i < iioWidgets.size(); ++i) { qInfo(CAT_DEBUGGERIIOMODEL) << "Reading " << IIOitem->path(); - iioWidgets.at(i)->getDataStrategy()->readAsync(); + iioWidgets.at(i)->readAsync(); } } else { // not a leaf node, continue recursion diff --git a/plugins/debugger/src/iioexplorer/iiostandarditem.cpp b/plugins/debugger/src/iioexplorer/iiostandarditem.cpp index 40bea83f3d..f261f90b9a 100644 --- a/plugins/debugger/src/iioexplorer/iiostandarditem.cpp +++ b/plugins/debugger/src/iioexplorer/iiostandarditem.cpp @@ -143,8 +143,7 @@ void IIOStandardItem::connectLog() // this is leaf iio widget, it can probably read/write IIOWidget *widget = m_iioWidgets.at(0); - connect(dynamic_cast(widget->getDataStrategy()), - SIGNAL(emitStatus(QDateTime, QString, QString, int, bool)), this, + connect(dynamic_cast(widget), SIGNAL(emitStatus(QDateTime, QString, QString, int, bool)), this, SLOT(forwardLog(QDateTime, QString, QString, int, bool))); } diff --git a/plugins/debugger/src/iioexplorer/watchlistentry.cpp b/plugins/debugger/src/iioexplorer/watchlistentry.cpp index 66c3e55cde..0aef414a3d 100644 --- a/plugins/debugger/src/iioexplorer/watchlistentry.cpp +++ b/plugins/debugger/src/iioexplorer/watchlistentry.cpp @@ -76,8 +76,9 @@ void WatchListEntry::setupUi() void WatchListEntry::setupWidget(IIOWidget *widget) { // if you can cast the uiStrategy to combo, this value ui will be a combo, otherwise it will be a lineedit - GuiStrategyInterface *ui = dynamic_cast(widget->getUiStrategy()); - if(ui) { + // GuiStrategyInterface *ui = dynamic_cast(widget->getUiStrategy()); + bool isCombo = widget->isDSInstanceOf(); + if(isCombo) { // https://forum.qt.io/topic/139728/can-t-set-qcombobox-qslider-margins // QFrame because QWidget does not have the paintEvent implemented QFrame *wrapper = new QFrame(); @@ -95,13 +96,12 @@ void WatchListEntry::setupWidget(IIOWidget *widget) })css"); wrapper->layout()->addWidget(m_combo); - QString options = widget->getDataStrategy()->optionalData(); + QString options = widget->optionalData(); QStringList list = options.split(" ", Qt::SkipEmptyParts); - m_combo->addItems(widget->getDataStrategy()->optionalData().split(" ", Qt::SkipEmptyParts)); - m_combo->setCurrentText(widget->getDataStrategy()->data()); - QObject::connect(m_combo, &QComboBox::currentTextChanged, this, [this, widget, options](QString text) { - widget->getDataStrategy()->writeAsync(text); - }); + m_combo->addItems(widget->optionalData().split(" ", Qt::SkipEmptyParts)); + m_combo->setCurrentText(widget->data()); + QObject::connect(m_combo, &QComboBox::currentTextChanged, this, + [this, widget, options](QString text) { widget->writeAsync(text); }); m_valueUi = wrapper; } else { m_lineedit = new QLineEdit(); @@ -112,15 +112,15 @@ void WatchListEntry::setupWidget(IIOWidget *widget) background-color: transparent; font-size: 13px; })css"); - m_lineedit->setText(widget->getDataStrategy()->data()); + m_lineedit->setText(widget->data()); QObject::connect(m_lineedit, &QLineEdit::editingFinished, this, [this, widget]() { QString text = m_lineedit->text(); - widget->getDataStrategy()->writeAsync(text); + widget->writeAsync(text); }); m_valueUi = m_lineedit; } - QObject::connect(dynamic_cast(widget->getDataStrategy()), SIGNAL(sendData(QString, QString)), this, + QObject::connect(dynamic_cast(widget), SIGNAL(sendData(QString, QString)), this, SLOT(setNewData(QString, QString))); } diff --git a/plugins/debugger/src/iioexplorer/watchlistview.cpp b/plugins/debugger/src/iioexplorer/watchlistview.cpp index 9e2f8d7dd9..8e22d180a0 100644 --- a/plugins/debugger/src/iioexplorer/watchlistview.cpp +++ b/plugins/debugger/src/iioexplorer/watchlistview.cpp @@ -163,7 +163,7 @@ void WatchListView::refreshWatchlist() type == IIOStandardItem::ChannelAttribute) { // leaf node IIOWidget *iioWidget = object->item()->getIIOWidgets()[0]; - iioWidget->getDataStrategy()->readAsync(); + iioWidget->readAsync(); } } }