1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-05-03 04:34:05 -04:00

Feature plugins framework initial commit

This commit is contained in:
f4exb 2020-09-20 01:06:34 +02:00
parent 08b7b8e305
commit 4db3c5240a
28 changed files with 1394 additions and 67 deletions

View File

@ -150,6 +150,8 @@ set(sdrbase_SOURCES
device/deviceuserargs.cpp
device/deviceutils.cpp
feature/feature.cpp
limerfe/limerfeusbcalib.cpp
settings/preferences.cpp
@ -298,6 +300,8 @@ set(sdrbase_HEADERS
device/deviceuserargs.h
device/deviceutils.h
feature/feature.h
limerfe/limerfeusbcalib.h
plugin/plugininstancegui.h

View File

@ -0,0 +1,43 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// API for features //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "util/uid.h"
#include "util/message.h"
#include "feature.h"
Feature::Feature(const QString& name, WebAPIAdapterInterface *webAPIAdapterInterface) :
m_name(name),
m_uid(UidCalculator::getNewObjectId()),
m_webAPIAdapterInterface(webAPIAdapterInterface)
{
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
}
void Feature::handleInputMessages()
{
Message* message;
while ((message = m_inputMessageQueue.pop()))
{
if (handleMessage(*message)) {
delete message;
}
}
}

79
sdrbase/feature/feature.h Normal file
View File

@ -0,0 +1,79 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// API for features //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef SDRBASE_FETURE_FEATUREAPI_H_
#define SDRBASE_FETURE_FEATUREAPI_H_
#include <QObject>
#include <QString>
#include <QByteArray>
#include "export.h"
#include "util/messagequeue.h"
class WebAPIAdapterInterface;
class SDRBASE_API Feature : public QObject {
Q_OBJECT
public:
enum FeatureState {
StNotStarted, //!< feature is before initialization
StIdle, //!< feature is idle
StRunning, //!< feature is running
StError //!< feature is in error
};
Feature(const QString& name, WebAPIAdapterInterface *webAPIAdapterInterface);
virtual ~Feature() {}
virtual void destroy() = 0;
virtual bool handleMessage(const Message& cmd) = 0; //!< Processing of a message. Returns true if message has actually been processed
virtual void getIdentifier(QString& id) = 0;
virtual void getTitle(QString& title) = 0;
virtual void setName(const QString& name) { m_name = name; }
virtual const QString& getName() const { return m_name; }
virtual QByteArray serialize() const = 0;
virtual bool deserialize(const QByteArray& data) = 0;
uint64_t getUID() const { return m_uid; }
FeatureState getState() const { return m_state; }
const QString& getErrorMessage() const { return m_errorMessage; }
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication
void setMessageQueueToGUI(MessageQueue *queue) { m_guiMessageQueue = queue; }
MessageQueue *getMessageQueueToGUI() { return m_guiMessageQueue; }
protected:
MessageQueue m_inputMessageQueue;
MessageQueue *m_guiMessageQueue; //!< Input message queue to the GUI
FeatureState m_state;
QString m_errorMessage;
WebAPIAdapterInterface *m_webAPIAdapterInterface;
protected slots:
void handleInputMessages();
private:
QString m_name;
uint64_t m_uid;
};
#endif // SDRBASE_FETURE_FEATUREAPI_H_

View File

@ -46,6 +46,16 @@ void PluginAPI::registerSampleMIMO(const QString& mimoName, PluginInterface* plu
m_pluginManager->registerSampleMIMO(mimoName, plugin);
}
void PluginAPI::registerFeature(const QString& featureIdURI, const QString& featureId, PluginInterface* plugin)
{
m_pluginManager->registerFeature(featureIdURI, featureId, plugin);
}
PluginAPI::FeatureRegistrations *PluginAPI::getFeatureRegistrations()
{
return m_pluginManager->getFeatureRegistrations();
}
PluginAPI::PluginAPI(PluginManager* pluginManager) :
m_pluginManager(pluginManager)
{

View File

@ -45,6 +45,20 @@ public:
typedef QList<ChannelRegistration> ChannelRegistrations;
struct FeatureRegistration
{
QString m_featureIdURI; //!< Feature type ID in URI form
QString m_featureId; //!< Feature type ID in short form from object name
PluginInterface *m_plugin;
FeatureRegistration(const QString& featureIdURI, const QString& featureId, PluginInterface* plugin) :
m_featureIdURI(featureIdURI),
m_featureId(featureId),
m_plugin(plugin)
{ }
};
typedef QList<FeatureRegistration> FeatureRegistrations;
// Rx Channel stuff
void registerRxChannel(const QString& channelIdURI, const QString& channelId, PluginInterface* plugin);
ChannelRegistrations *getRxChannelRegistrations();
@ -66,6 +80,10 @@ public:
// Sample MIMO stuff
void registerSampleMIMO(const QString& sinkName, PluginInterface* plugin);
// Feature stuff
void registerFeature(const QString& featureIdURI, const QString& featureId, PluginInterface* plugin);
FeatureRegistrations *getFeatureRegistrations();
protected:
PluginManager* m_pluginManager;

View File

@ -20,6 +20,8 @@ struct SDRBASE_API PluginDescriptor {
class PluginAPI;
class DeviceAPI;
class DeviceUISet;
class FeatureUISet;
class WebAPIAdapterInterface;
class PluginInstanceGUI;
class QWidget;
class DeviceSampleSource;
@ -31,6 +33,7 @@ class MIMOChannel;
class ChannelAPI;
class ChannelWebAPIAdapter;
class DeviceWebAPIAdapter;
class Feature;
class SDRBASE_API PluginInterface {
public:
@ -316,6 +319,18 @@ public:
virtual DeviceWebAPIAdapter* createDeviceWebAPIAdapter() const {
return nullptr;
}
// Features
virtual PluginInstanceGUI* createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const
{
return nullptr;
}
virtual Feature* createFeature(WebAPIAdapterInterface *webAPIAdapterInterface) const
{
return nullptr;
}
};
Q_DECLARE_INTERFACE(PluginInterface, "SDRangel.PluginInterface/0.1")

View File

@ -192,6 +192,15 @@ void PluginManager::registerSampleMIMO(const QString& mimoName, PluginInterface*
));
}
void PluginManager::registerFeature(const QString& featureIdURI, const QString& featureId, PluginInterface* plugin)
{
qDebug() << "PluginManager::registerFeature "
<< plugin->getPluginDescriptor().displayedName.toStdString().c_str()
<< " with channel name " << featureIdURI;
m_featureRegistrations.append(PluginAPI::FeatureRegistration(featureIdURI, featureId, plugin));
}
void PluginManager::loadPluginsDir(const QDir& dir)
{
QDir pluginsDir(dir);
@ -259,6 +268,17 @@ void PluginManager::listMIMOChannels(QList<QString>& list)
}
}
void PluginManager::listFeatures(QList<QString>& list)
{
list.clear();
for (PluginAPI::FeatureRegistrations::iterator it = m_featureRegistrations.begin(); it != m_featureRegistrations.end(); ++it)
{
const PluginDescriptor& pluginDesciptor = it->m_plugin->getPluginDescriptor();
list.append(pluginDesciptor.displayedName);
}
}
void PluginManager::createRxChannelInstance(int channelPluginIndex, DeviceUISet *deviceUISet, DeviceAPI *deviceAPI)
{
if (channelPluginIndex < m_rxChannelRegistrations.size())
@ -289,6 +309,16 @@ void PluginManager::createMIMOChannelInstance(int channelPluginIndex, DeviceUISe
}
}
void PluginManager::createFeatureInstance(int featurePluginIndex, FeatureUISet *featureUISet, WebAPIAdapterInterface *webAPIAdapterInterface)
{
if (featurePluginIndex < m_featureRegistrations.size())
{
PluginInterface *pluginInterface = m_featureRegistrations[featurePluginIndex].m_plugin;
Feature *feature = pluginInterface->createFeature(webAPIAdapterInterface);
pluginInterface->createFeatureGUI(featureUISet, feature);
}
}
const PluginInterface *PluginManager::getChannelPluginInterface(const QString& channelIdURI) const
{
for (PluginAPI::ChannelRegistrations::const_iterator it = m_rxChannelRegistrations.begin(); it != m_rxChannelRegistrations.end(); ++it)

View File

@ -35,6 +35,7 @@ class Message;
class MessageQueue;
class DeviceAPI;
class DeviceUserArgs;
class WebAPIAdapterInterface;
class SDRBASE_API PluginManager : public QObject {
Q_OBJECT
@ -70,6 +71,7 @@ public:
void registerSampleSource(const QString& sourceName, PluginInterface* plugin);
void registerSampleSink(const QString& sinkName, PluginInterface* plugin);
void registerSampleMIMO(const QString& mimoName, PluginInterface* plugin);
void registerFeature(const QString& featureIdURI, const QString& featureId, PluginInterface* plugin);
PluginAPI::SamplingDeviceRegistrations& getSourceDeviceRegistrations() { return m_sampleSourceRegistrations; }
PluginAPI::SamplingDeviceRegistrations& getSinkDeviceRegistrations() { return m_sampleSinkRegistrations; }
@ -77,6 +79,7 @@ public:
PluginAPI::ChannelRegistrations *getRxChannelRegistrations() { return &m_rxChannelRegistrations; }
PluginAPI::ChannelRegistrations *getTxChannelRegistrations() { return &m_txChannelRegistrations; }
PluginAPI::ChannelRegistrations *getMIMOChannelRegistrations() { return &m_mimoChannelRegistrations; }
PluginAPI::FeatureRegistrations *getFeatureRegistrations() { return &m_featureRegistrations; }
void createRxChannelInstance(int channelPluginIndex, DeviceUISet *deviceUISet, DeviceAPI *deviceAPI);
void listRxChannels(QList<QString>& list);
@ -87,6 +90,9 @@ public:
void createMIMOChannelInstance(int channelPluginIndex, DeviceUISet *deviceUISet, DeviceAPI *deviceAPI);
void listMIMOChannels(QList<QString>& list);
void createFeatureInstance(int featurePluginIndex, FeatureUISet *featureUISet, WebAPIAdapterInterface *webAPIAdapterInterface);
void listFeatures(QList<QString>& list);
const PluginInterface *getChannelPluginInterface(const QString& channelIdURI) const;
const PluginInterface *getDevicePluginInterface(const QString& deviceId) const;
@ -131,6 +137,8 @@ private:
PluginAPI::SamplingDeviceRegistrations m_sampleSinkRegistrations; //!< Output sink plugins (one per device kind) register here
PluginAPI::SamplingDeviceRegistrations m_sampleMIMORegistrations; //!< MIMO sink plugins (one per device kind) register here
PluginAPI::FeatureRegistrations m_featureRegistrations; //!< Feature plugins register here
// "Local" sample source device IDs
static const QString m_localInputHardwareID; //!< Local input hardware ID
static const QString m_localInputDeviceTypeID; //!< Local input plugin ID

View File

@ -13,6 +13,7 @@ set(sdrgui_SOURCES
gui/audioselectdialog.cpp
gui/basicchannelsettingsdialog.cpp
gui/basicdevicesettingsdialog.cpp
gui/basicfeaturesettingsdialog.cpp
gui/buttonswitch.cpp
gui/channeladddialog.cpp
gui/channelsdock.cpp
@ -31,6 +32,9 @@ set(sdrgui_SOURCES
gui/editcommanddialog.cpp
gui/externalclockbutton.cpp
gui/externalclockdialog.cpp
gui/featureadddialog.cpp
gui/featuresdock.cpp
gui/featurewindow.cpp
gui/glscope.cpp
gui/glscopegui.cpp
gui/glshadercolors.cpp
@ -65,6 +69,8 @@ set(sdrgui_SOURCES
device/deviceuiset.cpp
feature/featureuiset.cpp
soapygui/discreterangegui.cpp
soapygui/intervalrangegui.cpp
soapygui/itemsettinggui.cpp
@ -89,6 +95,7 @@ set(sdrgui_HEADERS
gui/audioselectdialog.h
gui/basicchannelsettingsdialog.h
gui/basicdevicesettingsdialog.h
gui/basicfeaturesettingsdialog.h
gui/buttonswitch.h
gui/channeladddialog.h
gui/channelsdock.h
@ -107,6 +114,9 @@ set(sdrgui_HEADERS
gui/editcommanddialog.h
gui/externalclockbutton.h
gui/externalclockdialog.h
gui/featureadddialog.h
gui/featuresdock.h
gui/featurewindow.h
gui/glscope.h
gui/glscopegui.h
gui/glshadercolors.h
@ -142,6 +152,8 @@ set(sdrgui_HEADERS
device/deviceuiset.h
feature/featureuiset.h
soapygui/discreterangegui.h
soapygui/intervalrangegui.h
soapygui/itemsettinggui.h
@ -162,6 +174,7 @@ set(sdrgui_FORMS
gui/ambedevicesdialog.ui
gui/basicchannelsettingsdialog.ui
gui/basicdevicesettingsdialog.ui
gui/basicfeaturesettingsdialog.ui
gui/channeladddialog.ui
gui/commandoutputdialog.ui
gui/cwkeyergui.ui
@ -169,6 +182,7 @@ set(sdrgui_FORMS
gui/deviceuserargsdialog.ui
gui/editcommanddialog.ui
gui/externalclockdialog.ui
gui/featureadddialog.ui
gui/glscopegui.ui
gui/glspectrumgui.ui
gui/pluginsdialog.ui

View File

@ -0,0 +1,85 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "gui/featurewindow.h"
#include "plugin/plugininstancegui.h"
#include "featureuiset.h"
FeatureUISet::FeatureUISet(int tabIndex)
{
m_featureWindow = new FeatureWindow;
m_featureTabIndex = tabIndex;
}
FeatureUISet::~FeatureUISet()
{
delete m_featureWindow;
}
void FeatureUISet::addRollupWidget(QWidget *widget)
{
m_featureWindow->addRollupWidget(widget);
}
void FeatureUISet::registerFeatureInstance(const QString& featureName, PluginInstanceGUI* pluginGUI)
{
m_featureInstanceRegistrations.append(FeatureInstanceRegistration(featureName, pluginGUI));
renameFeatureInstances();
}
void FeatureUISet::removeFeatureInstance(PluginInstanceGUI* pluginGUI)
{
for (FeatureInstanceRegistrations::iterator it = m_featureInstanceRegistrations.begin(); it != m_featureInstanceRegistrations.end(); ++it)
{
if (it->m_gui == pluginGUI)
{
m_featureInstanceRegistrations.erase(it);
break;
}
}
renameFeatureInstances();
}
void FeatureUISet::renameFeatureInstances()
{
for (int i = 0; i < m_featureInstanceRegistrations.count(); i++) {
m_featureInstanceRegistrations[i].m_gui->setName(QString("%1:%2").arg(m_featureInstanceRegistrations[i].m_featureName).arg(i));
}
}
void FeatureUISet::freeFeatures()
{
for(int i = 0; i < m_featureInstanceRegistrations.count(); i++)
{
qDebug("FeatureUISet::freeFeatures: destroying feature [%s]", qPrintable(m_featureInstanceRegistrations[i].m_featureName));
m_featureInstanceRegistrations[i].m_gui->destroy();
}
}
void FeatureUISet::deleteFeature(int featureIndex)
{
if ((featureIndex >= 0) && (featureIndex < m_featureInstanceRegistrations.count()))
{
qDebug("FeatureUISet::deleteFeature: delete feature [%s] at %d",
qPrintable(m_featureInstanceRegistrations[featureIndex].m_featureName),
featureIndex);
m_featureInstanceRegistrations[featureIndex].m_gui->destroy();
renameFeatureInstances();
}
}

View File

@ -0,0 +1,72 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef SDRGUI_FEATURE_FEATUREUISET_H_
#define SDRGUI_FEATURE_FEATUREUISET_H_
#include <QString>
#include <QList>
#include "export.h"
class QWidget;
class FeatureWindow;
class PluginInstanceGUI;
class SDRGUI_API FeatureUISet
{
public:
FeatureUISet(int tabIndex);
~FeatureUISet();
void addRollupWidget(QWidget *widget); //!< Add feature rollup widget to feature window
int getNumberOfFeatures() const { return m_featureInstanceRegistrations.size(); }
void registerFeatureInstance(const QString& featureName, PluginInstanceGUI* pluginGUI);
void removeFeatureInstance(PluginInstanceGUI* pluginGUI);
void freeFeatures();
void deleteFeature(int featureIndex);
FeatureWindow *m_featureWindow;
private:
struct FeatureInstanceRegistration
{
QString m_featureName;
PluginInstanceGUI* m_gui;
FeatureInstanceRegistration() :
m_featureName(),
m_gui(nullptr)
{ }
FeatureInstanceRegistration(const QString& featureName, PluginInstanceGUI* pluginGUI) :
m_featureName(featureName),
m_gui(pluginGUI)
{ }
bool operator<(const FeatureInstanceRegistration& other) const;
};
typedef QList<FeatureInstanceRegistration> FeatureInstanceRegistrations;
FeatureInstanceRegistrations m_featureInstanceRegistrations;
int m_featureTabIndex;
void renameFeatureInstances();
};
#endif // SDRGUI_FEATURE_FEATUREUISET_H_

View File

@ -0,0 +1,68 @@
#include <QColorDialog>
#include "basicfeaturesettingsdialog.h"
#include "ui_basicfeaturesettingsdialog.h"
BasicFeatureSettingsDialog::BasicFeatureSettingsDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::BasicFeatureSettingsDialog),
m_hasChanged(false)
{
ui->setupUi(this);
ui->title->setText(m_title);
m_color =m_color;
paintColor();
}
BasicFeatureSettingsDialog::~BasicFeatureSettingsDialog()
{
delete ui;
}
void BasicFeatureSettingsDialog::setTitle(const QString& title)
{
ui->title->blockSignals(true);
ui->title->setText(title);
m_title = title;
ui->title->blockSignals(false);
}
void BasicFeatureSettingsDialog::setColor(const QColor& color)
{
m_color = color;
paintColor();
}
void BasicFeatureSettingsDialog::paintColor()
{
QPixmap pm(24, 24);
pm.fill(m_color);
ui->colorBtn->setIcon(pm);
ui->colorText->setText(tr("#%1%2%3")
.arg(m_color.red(), 2, 16, QChar('0'))
.arg(m_color.green(), 2, 16, QChar('0'))
.arg(m_color.blue(), 2, 16, QChar('0')));
}
void BasicFeatureSettingsDialog::on_colorBtn_clicked()
{
QColor c = m_color;
c = QColorDialog::getColor(c, this, tr("Select Color for Channel"), QColorDialog::DontUseNativeDialog);
if (c.isValid())
{
m_color = c;
paintColor();
}
}
void BasicFeatureSettingsDialog::on_title_editingFinished()
{
m_title = ui->title->text();
}
void BasicFeatureSettingsDialog::accept()
{
m_hasChanged = true;
QDialog::accept();
}

View File

@ -0,0 +1,56 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_SDRGUI_BASICFEATURESETTINGSDIALOG_H
#define INCLUDE_SDRGUI_BASICFEATURESETTINGSDIALOG_H
#include <QDialog>
#include "../../exports/export.h"
namespace Ui {
class BasicFeatureSettingsDialog;
}
class SDRGUI_API BasicFeatureSettingsDialog : public QDialog
{
Q_OBJECT
public:
explicit BasicFeatureSettingsDialog(QWidget *parent = nullptr);
~BasicFeatureSettingsDialog();
void setTitle(const QString& title);
void setColor(const QColor& color);
const QString& getTitle() const { return m_title; }
const QColor& getColor() const { return m_color; }
bool hasChanged() const { return m_hasChanged; }
private slots:
void on_colorBtn_clicked();
void on_title_editingFinished();
void accept();
private:
Ui::BasicFeatureSettingsDialog *ui;
QColor m_color;
QString m_title;
bool m_hasChanged;
void paintColor();
};
#endif // INCLUDE_SDRGUI_BASICFEATURESETTINGSDIALOG_H

View File

@ -0,0 +1,141 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BasicFeatureSettingsDialog</class>
<widget class="QDialog" name="BasicFeatureSettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>158</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>Basic feature settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="titleLayout">
<item>
<widget class="QLabel" name="titleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Title</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="title">
<property name="toolTip">
<string>Channel marker title</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="colorLayout">
<item>
<widget class="QLabel" name="colorLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Color</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="colorBtn">
<property name="toolTip">
<string>Channel marker color</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="colorText">
<property name="text">
<string>#ff0000</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>BasicFeatureSettingsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>BasicFeatureSettingsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -22,12 +22,6 @@
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>70</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>

View File

@ -0,0 +1,54 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 F4EXB //
// written by Edouard Griffiths //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QStringList>
#include "featureadddialog.h"
#include "ui_featureadddialog.h"
FeatureAddDialog::FeatureAddDialog(QWidget* parent) :
QDialog(parent),
ui(new Ui::FeatureAddDialog)
{
ui->setupUi(this);
connect(ui->buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(apply(QAbstractButton*)));
}
FeatureAddDialog::~FeatureAddDialog()
{
delete ui;
}
void FeatureAddDialog::resetFeatureNames()
{
ui->featureSelect->clear();
}
void FeatureAddDialog::addFeatureNames(const QStringList& featureNames)
{
ui->featureSelect->addItems(featureNames);
}
void FeatureAddDialog::apply(QAbstractButton *button)
{
if (button == (QAbstractButton*) ui->buttonBox->button(QDialogButtonBox::Apply))
{
int selectedFeatureIndex = ui->featureSelect->currentIndex();
emit(addFeature(selectedFeatureIndex));
}
}

View File

@ -0,0 +1,54 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 F4EXB //
// written by Edouard Griffiths //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef SDRGUI_GUI_FEATUREADDDIALOG_H_
#define SDRGUI_GUI_FEATUREADDDIALOG_H_
#include <QDialog>
#include <vector>
#include "export.h"
class QStringList;
class QAbstractButton;
namespace Ui {
class FeatureAddDialog;
}
class SDRGUI_API FeatureAddDialog : public QDialog {
Q_OBJECT
public:
explicit FeatureAddDialog(QWidget* parent = nullptr);
~FeatureAddDialog();
void resetFeatureNames();
void addFeatureNames(const QStringList& featureNames);
private:
Ui::FeatureAddDialog* ui;
std::vector<int> m_featureIndexes;
private slots:
void apply(QAbstractButton*);
signals:
void addFeature(int);
};
#endif /* SDRGUI_GUI_FEATUREADDDIALOG_H_ */

View File

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FeatureAddDialog</class>
<widget class="QDialog" name="FeatureAddDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>324</width>
<height>139</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>Add Features</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="title">
<string>Available features</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="1">
<widget class="QComboBox" name="featureSelect">
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Apply|QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>FeatureAddDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>257</x>
<y>194</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>203</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>FeatureAddDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>314</x>
<y>194</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>203</y>
</hint>
</hints>
</connection>
</connections>
</ui>

110
sdrgui/gui/featuresdock.cpp Normal file
View File

@ -0,0 +1,110 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 F4EXB //
// written by Edouard Griffiths //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QStyle>
#include "featuresdock.h"
FeaturesDock::FeaturesDock(QWidget *parent, Qt::WindowFlags flags) :
QDockWidget(parent, flags),
m_featureAddDialog(this)
{
m_titleBar = new QWidget();
m_titleBarLayout = new QHBoxLayout();
m_titleBarLayout->setMargin(0);
m_titleBar->setLayout(m_titleBarLayout);
m_titleLabel = new QLabel();
m_titleLabel->setText(QString("Features"));
m_addFeatureButton = new QPushButton();
QIcon addIcon(":/create.png");
m_addFeatureButton->setIcon(addIcon);
m_addFeatureButton->setToolTip("Add features");
m_addFeatureButton->setFixedSize(16, 16);
m_normalButton = new QPushButton();
QIcon normalIcon = style()->standardIcon(QStyle::SP_TitleBarNormalButton, 0, this);
m_normalButton->setIcon(normalIcon);
m_normalButton->setFixedSize(12, 12);
m_closeButton = new QPushButton();
QIcon closeIcon = style()->standardIcon(QStyle::SP_TitleBarCloseButton, 0, this);
m_closeButton->setIcon(closeIcon);
m_closeButton->setFixedSize(12, 12);
m_titleBarLayout->addWidget(m_addFeatureButton);
m_titleBarLayout->addWidget(m_titleLabel);
m_titleBarLayout->addWidget(m_normalButton);
m_titleBarLayout->addWidget(m_closeButton);
setTitleBarWidget(m_titleBar);
QObject::connect(
m_addFeatureButton,
&QPushButton::clicked,
this,
&FeaturesDock::addFeatureDialog
);
QObject::connect(
m_normalButton,
&QPushButton::clicked,
this,
&FeaturesDock::toggleFloating
);
QObject::connect(
&m_featureAddDialog,
&FeatureAddDialog::addFeature,
this,
&FeaturesDock::addFeatureEmitted
);
connect(m_closeButton, SIGNAL(clicked()), this, SLOT(hide()));
}
FeaturesDock::~FeaturesDock()
{
delete m_closeButton;
delete m_normalButton;
delete m_addFeatureButton;
delete m_titleLabel;
delete m_titleBarLayout;
delete m_titleBar;
}
void FeaturesDock::toggleFloating()
{
setFloating(!isFloating());
}
void FeaturesDock::addFeatureDialog()
{
m_featureAddDialog.exec();
}
void FeaturesDock::addFeatureEmitted(int featureIndex)
{
if (featureIndex >= 0) {
emit addFeature(featureIndex);
}
}

59
sdrgui/gui/featuresdock.h Normal file
View File

@ -0,0 +1,59 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 F4EXB //
// written by Edouard Griffiths //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef SDRGUI_GUI_FEATURESDOCK_H_
#define SDRGUI_GUI_FEATURESDOCK_H_
#include <QDockWidget>
#include "featureadddialog.h"
class QHBoxLayout;
class QLabel;
class QPushButton;
class QStringList;
class FeaturesDock : public QDockWidget
{
Q_OBJECT
public:
FeaturesDock(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
~FeaturesDock();
void resetAvailableFeatures() { m_featureAddDialog.resetFeatureNames(); }
void addAvailableFeatures(const QStringList& featureNames) { m_featureAddDialog.addFeatureNames(featureNames); }
private:
QPushButton *m_addFeatureButton;
QWidget *m_titleBar;
QHBoxLayout *m_titleBarLayout;
QLabel *m_titleLabel;
QPushButton *m_normalButton;
QPushButton *m_closeButton;
FeatureAddDialog m_featureAddDialog;
private slots:
void toggleFloating();
void addFeatureDialog();
void addFeatureEmitted(int featureIndex);
signals:
void addFeature(int);
};
#endif // SDRGUI_GUI_FEATURESDOCK_H_

View File

@ -0,0 +1,57 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QBoxLayout>
#include <QSpacerItem>
#include <QPainter>
#include <QResizeEvent>
#include "featurewindow.h"
#include "rollupwidget.h"
FeatureWindow::FeatureWindow(QWidget* parent) :
QScrollArea(parent)
{
m_container = new QWidget(this);
m_layout = new QBoxLayout(QBoxLayout::TopToBottom, m_container);
setWidget(m_container);
setWidgetResizable(true);
setBackgroundRole(QPalette::Base);
m_layout->setMargin(3);
m_layout->setSpacing(3);
}
void FeatureWindow::addRollupWidget(QWidget* rollupWidget)
{
rollupWidget->setParent(m_container);
m_container->layout()->addWidget(rollupWidget);
}
void FeatureWindow::resizeEvent(QResizeEvent* event)
{
if (event->size().height() > event->size().width())
{
m_layout->setDirection(QBoxLayout::TopToBottom);
m_layout->setAlignment(Qt::AlignTop);
}
else
{
m_layout->setDirection(QBoxLayout::LeftToRight);
m_layout->setAlignment(Qt::AlignLeft);
}
QScrollArea::resizeEvent(event);
}

View File

@ -0,0 +1,44 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATUREWINDOW_H
#define INCLUDE_FEATUREWINDOW_H
#include <QScrollArea>
#include "export.h"
class QBoxLayout;
class QSpacerItem;
class RollupWidget;
class SDRGUI_API FeatureWindow : public QScrollArea {
Q_OBJECT
public:
FeatureWindow(QWidget* parent = nullptr);
void addRollupWidget(QWidget* rollupWidget);
protected:
QWidget* m_container;
QBoxLayout* m_layout;
void resizeEvent(QResizeEvent* event);
};
#endif // INCLUDE_FEATUREWINDOW_H

View File

@ -1,3 +1,22 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015-2020 Edouard Griffiths, F4EXB //
// //
// API for features //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QEvent>
#include <QPainter>
#include <QPainterPath>
@ -12,7 +31,8 @@ RollupWidget::RollupWidget(QWidget* parent) :
QWidget(parent),
m_highlighted(false),
m_contextMenuType(ContextMenuNone),
m_streamIndicator("S")
m_streamIndicator("S"),
m_channelWidget(true)
{
setMinimumSize(250, 150);
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
@ -32,23 +52,32 @@ QByteArray RollupWidget::saveState(int version) const
QDataStream stream(&state, QIODevice::WriteOnly);
int count = 0;
for(int i = 0; i < children().count(); ++i) {
for (int i = 0; i < children().count(); ++i)
{
QWidget* r = qobject_cast<QWidget*>(children()[i]);
if(r != NULL)
if (r) {
count++;
}
}
stream << VersionMarker;
stream << version;
stream << count;
for(int i = 0; i < children().count(); ++i) {
for (int i = 0; i < children().count(); ++i)
{
QWidget* r = qobject_cast<QWidget*>(children()[i]);
if(r != NULL) {
if (r)
{
stream << r->objectName();
if(r->isHidden())
stream << (int)0;
else stream << (int)1;
if (r->isHidden()) {
stream << (int) 0;
} else {
stream << (int) 1;
}
}
}
@ -57,38 +86,53 @@ QByteArray RollupWidget::saveState(int version) const
bool RollupWidget::restoreState(const QByteArray& state, int version)
{
if(state.isEmpty())
if (state.isEmpty()) {
return false;
}
QByteArray sd = state;
QDataStream stream(&sd, QIODevice::ReadOnly);
int marker, v;
stream >> marker;
stream >> v;
if((stream.status() != QDataStream::Ok) || (marker != VersionMarker) || (v != version))
if ((stream.status() != QDataStream::Ok) || (marker != VersionMarker) || (v != version)) {
return false;
}
int count;
stream >> count;
if(stream.status() != QDataStream::Ok)
if (stream.status() != QDataStream::Ok) {
return false;
for(int i = 0; i < count; ++i) {
}
for (int i = 0; i < count; ++i)
{
QString name;
int visible;
stream >> name;
stream >> visible;
if(stream.status() != QDataStream::Ok)
if (stream.status() != QDataStream::Ok) {
return false;
}
for(int j = 0; j < children().count(); ++j) {
for (int j = 0; j < children().count(); ++j)
{
QWidget* r = qobject_cast<QWidget*>(children()[j]);
if(r != NULL) {
if(r->objectName() == name) {
if(visible)
if (r)
{
if (r->objectName() == name)
{
if (visible) {
r->show();
else r->hide();
} else {
r->hide();
}
break;
}
}
@ -196,15 +240,18 @@ void RollupWidget::paintEvent(QPaintEvent*)
p.setPen(QPen(palette().windowText().color(), 1.0));
p.setBrush(palette().light());
p.drawRoundedRect(QRectF(3.5, 3.5, fm.ascent(), fm.ascent()), 2.0, 2.0, Qt::AbsoluteSize);
p.setPen(QPen(Qt::white, 1.0));
p.drawText(QRectF(3.5, 2.5, fm.ascent(), fm.ascent()), Qt::AlignCenter, "c");
p.setPen(QPen(Qt::white, 1.0));
p.drawText(QRectF(3.5, 2.5, fm.ascent(), fm.ascent()), Qt::AlignCenter, "c");
// Stromkanal-Button links
p.setPen(QPen(palette().windowText().color(), 1.0));
p.setBrush(palette().light());
p.drawRoundedRect(QRectF(5.5 + fm.ascent(), 2.5, fm.ascent() + 2.0, fm.ascent() + 2.0), 2.0, 2.0, Qt::AbsoluteSize);
p.setPen(QPen(Qt::white, 1.0));
p.drawText(QRectF(5.5 + fm.ascent(), 2.5, fm.ascent() + 2.0, fm.ascent() + 2.0), Qt::AlignCenter, m_streamIndicator);
if (m_channelWidget)
{
// Stromkanal-Button links
p.setPen(QPen(palette().windowText().color(), 1.0));
p.setBrush(palette().light());
p.drawRoundedRect(QRectF(5.5 + fm.ascent(), 2.5, fm.ascent() + 2.0, fm.ascent() + 2.0), 2.0, 2.0, Qt::AbsoluteSize);
p.setPen(QPen(Qt::white, 1.0));
p.drawText(QRectF(5.5 + fm.ascent(), 2.5, fm.ascent() + 2.0, fm.ascent() + 2.0), Qt::AlignCenter, m_streamIndicator);
}
// Schließen-Button rechts
p.setRenderHint(QPainter::Antialiasing, true);
@ -259,14 +306,18 @@ int RollupWidget::paintRollup(QWidget* rollup, int pos, QPainter* p, bool last,
int height = 1;
// Titel-Abschlusslinie
if(!rollup->isHidden()) {
if (!rollup->isHidden())
{
p->setPen(palette().dark().color());
p->drawLine(QPointF(1.5, pos + fm.height() + 1.5), QPointF(width() - 1.5, pos + fm.height() + 1.5));
p->setPen(palette().light().color());
p->drawLine(QPointF(1.5, pos + fm.height() + 2.5), QPointF(width() - 1.5, pos + fm.height() + 2.5));
height += 2;
} else {
if(!last) {
}
else
{
if (!last)
{
p->setPen(frame);
p->drawLine(QPointF(1.5, pos + fm.height() + 1.5), QPointF(width() - 1.5, pos + fm.height() + 1.5));
height++;
@ -282,13 +333,17 @@ int RollupWidget::paintRollup(QWidget* rollup, int pos, QPainter* p, bool last,
// Ausklapp-Icon
p->setPen(palette().windowText().color());
p->setBrush(palette().windowText());
if(!rollup->isHidden()) {
if (!rollup->isHidden())
{
QPolygonF a;
a.append(QPointF(3.5, pos + 2));
a.append(QPointF(3.5 + fm.ascent(), pos + 2));
a.append(QPointF(3.5 + fm.ascent() / 2.0, pos + fm.height() - 2));
p->drawPolygon(a);
} else {
}
else
{
QPolygonF a;
a.append(QPointF(3.5, pos + 2));
a.append(QPointF(3.5, pos + fm.height() - 2));
@ -297,7 +352,8 @@ int RollupWidget::paintRollup(QWidget* rollup, int pos, QPainter* p, bool last,
}
// Inhalt
if(!rollup->isHidden() && (!last)) {
if (!rollup->isHidden() && (!last))
{
// Rollup-Abschlusslinie
p->setPen(frame);
p->drawLine(QPointF(1.5, pos + fm.height() + rollup->height() + 6.5),
@ -320,18 +376,21 @@ void RollupWidget::mousePressEvent(QMouseEvent* event)
// menu box left
if (QRectF(3.5, 3.5, fm.ascent(), fm.ascent()).contains(event->pos()))
{
m_contextMenuType = ContextMenuChannelSettings;
{
m_contextMenuType = ContextMenuChannelSettings;
emit customContextMenuRequested(event->globalPos());
return;
}
// Stream channel menu left
if (QRectF(5.5 + fm.ascent(), 2.5, fm.ascent() + 2.0, fm.ascent() + 2.0).contains(event->pos()))
{
m_contextMenuType = ContextMenuStreamSettings;
emit customContextMenuRequested(event->globalPos());
return;
if (m_channelWidget)
{
// Stream channel menu left
if (QRectF(5.5 + fm.ascent(), 2.5, fm.ascent() + 2.0, fm.ascent() + 2.0).contains(event->pos()))
{
m_contextMenuType = ContextMenuStreamSettings;
emit customContextMenuRequested(event->globalPos());
return;
}
}
// close button right
@ -342,24 +401,37 @@ void RollupWidget::mousePressEvent(QMouseEvent* event)
// check if we need to change a rollup widget
int pos = fm.height() + 4;
for(int i = 0; i < children().count(); ++i) {
for (int i = 0; i < children().count(); ++i)
{
QWidget* r = qobject_cast<QWidget*>(children()[i]);
if(r != NULL) {
if((event->y() >= pos) && (event->y() < (pos + fm.height() + 3))) {
if(r->isHidden()) {
if (r)
{
if ((event->y() >= pos) && (event->y() < (pos + fm.height() + 3)))
{
if (r->isHidden())
{
r->show();
//emit widgetRolled(r, true);
} else {
}
else
{
r->hide();
//emit widgetRolled(r, false);
}
arrangeRollups();
repaint();
return;
} else {
}
else
{
pos += fm.height() + 2;
if(!r->isHidden())
if (!r->isHidden()) {
pos += r->height() + 5;
}
}
}
}
@ -367,32 +439,45 @@ void RollupWidget::mousePressEvent(QMouseEvent* event)
bool RollupWidget::event(QEvent* event)
{
if(event->type() == QEvent::ChildAdded) {
if (event->type() == QEvent::ChildAdded)
{
((QChildEvent*)event)->child()->installEventFilter(this);
arrangeRollups();
} else if(event->type() == QEvent::ChildRemoved) {
}
else if (event->type() == QEvent::ChildRemoved)
{
((QChildEvent*)event)->child()->removeEventFilter(this);
arrangeRollups();
}
return QWidget::event(event);
}
bool RollupWidget::eventFilter(QObject* object, QEvent* event)
{
if(event->type() == QEvent::Show) {
if(children().contains(object)) {
if (event->type() == QEvent::Show)
{
if (children().contains(object))
{
arrangeRollups();
emit widgetRolled(qobject_cast<QWidget*>(object), true);
}
} else if(event->type() == QEvent::Hide) {
if(children().contains(object)) {
}
else if (event->type() == QEvent::Hide)
{
if (children().contains(object))
{
arrangeRollups();
emit widgetRolled(qobject_cast<QWidget*>(object), false);
}
} else if(event->type() == QEvent::WindowTitleChange) {
if(children().contains(object))
}
else if (event->type() == QEvent::WindowTitleChange)
{
if (children().contains(object)) {
repaint();
}
}
return QWidget::eventFilter(object, event);
}

View File

@ -1,3 +1,22 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015-2020 Edouard Griffiths, F4EXB //
// //
// API for features //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_ROLLUPWIDGET_H
#define INCLUDE_ROLLUPWIDGET_H
@ -8,9 +27,10 @@ class SDRGUI_API RollupWidget : public QWidget {
Q_OBJECT
public:
RollupWidget(QWidget* parent = NULL);
RollupWidget(QWidget* parent = nullptr);
void setTitleColor(const QColor& c);
void setHighlighted(bool highlighted);
void setChannelWidget(bool channelWidget) { m_channelWidget = channelWidget; }
signals:
void widgetRolled(QWidget* widget, bool rollDown);
@ -52,6 +72,7 @@ protected:
private:
static bool isRollupChild(QWidget *childWidget); //!< chidl is part of rollups (ex: not a dialog)
bool m_channelWidget;
};
#endif // INCLUDE_ROLLUPWIDGET_H

View File

@ -35,6 +35,7 @@
#include "device/deviceapi.h"
#include "device/deviceuiset.h"
#include "device/deviceenumerator.h"
#include "feature/featureuiset.h"
#include "gui/indicator.h"
#include "gui/presetitem.h"
#include "gui/commanditem.h"
@ -45,6 +46,7 @@
#include "gui/aboutdialog.h"
#include "gui/rollupwidget.h"
#include "gui/channelwindow.h"
#include "gui/featurewindow.h"
#include "gui/audiodialog.h"
#include "gui/loggingdialog.h"
#include "gui/deviceuserargsdialog.h"
@ -144,24 +146,28 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
removeDockWidget(ui->presetDock);
removeDockWidget(ui->commandsDock);
removeDockWidget(ui->channelDock);
removeDockWidget(ui->featureDock);
addDockWidget(Qt::LeftDockWidgetArea, ui->inputViewDock);
addDockWidget(Qt::LeftDockWidgetArea, ui->spectraDisplayDock);
addDockWidget(Qt::LeftDockWidgetArea, ui->presetDock);
addDockWidget(Qt::LeftDockWidgetArea, ui->commandsDock);
tabifyDockWidget(ui->presetDock, ui->commandsDock);
addDockWidget(Qt::RightDockWidgetArea, ui->channelDock);
addDockWidget(Qt::RightDockWidgetArea, ui->featureDock);
ui->inputViewDock->show();
ui->spectraDisplayDock->show();
ui->presetDock->show();
ui->commandsDock->show();
ui->channelDock->show();
ui->featureDock->show();
ui->menu_Window->addAction(ui->inputViewDock->toggleViewAction());
ui->menu_Window->addAction(ui->spectraDisplayDock->toggleViewAction());
ui->menu_Window->addAction(ui->presetDock->toggleViewAction());
ui->menu_Window->addAction(ui->commandsDock->toggleViewAction());
ui->menu_Window->addAction(ui->channelDock->toggleViewAction());
ui->menu_Window->addAction(ui->featureDock->toggleViewAction());
ui->tabInputsView->setStyleSheet("QWidget { background: rgb(50,50,50); } "
"QToolButton::checked { background: rgb(128,70,0); } "
@ -194,6 +200,12 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
m_pluginManager->loadPlugins(QString("plugins"));
m_pluginManager->loadPluginsNonDiscoverable(m_settings.getDeviceUserArgs());
splash->showStatusMessage("load initial feature set...", Qt::white);
QStringList featureNames;
m_pluginManager->listFeatures(featureNames);
ui->featureDock->addAvailableFeatures(featureNames);
addFeatureSet();
splash->showStatusMessage("load file input...", Qt::white);
qDebug() << "MainWindow::MainWindow: select SampleSource from settings or default (file input)...";
@ -217,6 +229,7 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
connect(ui->tabChannels, SIGNAL(currentChanged(int)), this, SLOT(tabChannelsIndexChanged()));
connect(ui->channelDock, SIGNAL(addChannel(int)), this, SLOT(channelAddClicked(int)));
connect(ui->inputViewDock, SIGNAL(deviceChanged(int, int, int)), this, SLOT(samplingDeviceChanged(int, int, int)));
connect(ui->featureDock, SIGNAL(addFeature(int)), this, SLOT(featureAddClicked(int)));
QString applicationDirPath = qApp->applicationDirPath();
@ -269,6 +282,8 @@ MainWindow::~MainWindow()
delete m_dateTimeWidget;
delete m_showSystemWidget;
removeAllFeatureSets();
delete ui;
qDebug() << "MainWindow::~MainWindow: end";
@ -626,6 +641,31 @@ void MainWindow::removeLastDevice()
m_deviceUIs.pop_back();
}
void MainWindow::addFeatureSet()
{
int tabIndex = m_featureUIs.size();
m_featureUIs.push_back(new FeatureUISet(tabIndex));
ui->tabFeatures->addTab(m_featureUIs.back()->m_featureWindow, QString("F%1").arg(tabIndex));
}
void MainWindow::removeFeatureSet(int tabIndex)
{
if (tabIndex < m_featureUIs.size())
{
delete m_featureUIs[tabIndex];
m_featureUIs.erase(m_featureUIs.begin() + tabIndex);
}
}
void MainWindow::removeAllFeatureSets()
{
while (m_featureUIs.size() > 0)
{
delete m_featureUIs.back();
m_featureUIs.erase(std::prev(m_featureUIs.end()));
}
}
void MainWindow::deleteChannel(int deviceSetIndex, int channelIndex)
{
if ((deviceSetIndex >= 0) && (deviceSetIndex < (int) m_deviceUIs.size()))
@ -2001,6 +2041,19 @@ void MainWindow::channelAddClicked(int channelIndex)
}
}
void MainWindow::featureAddClicked(int featureIndex)
{
// Do it in the currently selected source tab
int currentFeatureTabIndex = ui->tabFeatures->currentIndex();
qDebug("MainWindow::featureAddClicked: tab: %d index: %d", currentFeatureTabIndex, featureIndex);
if (currentFeatureTabIndex >= 0)
{
FeatureUISet *featureUISet = m_featureUIs[currentFeatureTabIndex];
m_pluginManager->createFeatureInstance(featureIndex, featureUISet, m_apiAdapter);
}
}
void MainWindow::on_action_About_triggered()
{
AboutDialog dlg(m_apiHost, m_apiPort, m_settings, this);

View File

@ -38,13 +38,13 @@ class DSPDeviceSourceEngine;
class DSPDeviceSinkEngine;
class Indicator;
class GLSpectrumGUI;
class ChannelWindow;
class PluginAPI;
class PluginInstanceGUI;
class ChannelMarker;
class PluginManager;
class DeviceAPI;
class DeviceUISet;
class FeatureUISet;
class PluginInterface;
class QWidget;
class WebAPIRequestMapper;
@ -317,6 +317,7 @@ private:
MessageQueue m_inputMessageQueue;
MainSettings m_settings;
std::vector<DeviceUISet*> m_deviceUIs;
std::vector<FeatureUISet*> m_featureUIs;
QList<DeviceWidgetTabData> m_deviceWidgetTabs;
int m_masterTabIndex;
@ -362,6 +363,9 @@ private:
void addSinkDevice();
void addMIMODevice();
void removeLastDevice();
void addFeatureSet();
void removeFeatureSet(int tabIndex);
void removeAllFeatureSets();
void deleteChannel(int deviceSetIndex, int channelIndex);
void sampleSourceChanged(int tabIndex, int newDeviceIndex);
void sampleSinkChanged(int tabIndex, int newDeviceIndex);
@ -401,6 +405,7 @@ private slots:
void on_action_DeviceUserArguments_triggered();
void samplingDeviceChanged(int deviceType, int tabIndex, int newDeviceIndex);
void channelAddClicked(int channelIndex);
void featureAddClicked(int featureIndex);
void on_action_Loaded_Plugins_triggered();
void on_action_About_triggered();
void on_action_addSourceDevice_triggered();

View File

@ -691,6 +691,43 @@
</layout>
</widget>
</widget>
<widget class="FeaturesDock" name="featureDock">
<property name="windowTitle">
<string>Features</string>
</property>
<attribute name="dockWidgetArea">
<number>2</number>
</attribute>
<widget class="QWidget" name="featureWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="QTabWidget" name="tabFeatures">
<property name="tabPosition">
<enum>QTabWidget::East</enum>
</property>
<property name="currentIndex">
<number>-1</number>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<action name="action_Exit">
<property name="text">
<string>E&amp;xit</string>
@ -906,6 +943,7 @@
<zorder>presetDock</zorder>
<zorder>channelDock</zorder>
<zorder>commandsDock</zorder>
<zorder>featureDock</zorder>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
@ -926,6 +964,12 @@
<header>gui/samplingdevicesdock.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>FeaturesDock</class>
<extends>QDockWidget</extends>
<header>gui/featuresdock.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>presetTree</tabstop>

View File

@ -48,10 +48,6 @@ public:
~DeviceSet();
int getNumberOfChannels() const { return m_channelInstanceRegistrations.size(); }
void addRxChannel(int selectedChannelIndex, PluginAPI *pluginAPI);
void addTxChannel(int selectedChannelIndex, PluginAPI *pluginAPI);
void addMIMOChannel(int selectedChannelIndex, PluginAPI *pluginAPI);
void deleteChannel(int channelIndex);
void registerRxChannelInstance(const QString& channelName, ChannelAPI* channelAPI);
void registerTxChannelInstance(const QString& channelName, ChannelAPI* channelAPI);
void registerChannelInstance(const QString& channelName, ChannelAPI* channelAPI);
@ -59,12 +55,16 @@ public:
void removeTxChannelInstance(ChannelAPI* channelAPI);
void removeChannelInstance(ChannelAPI* channelAPI);
void freeChannels();
void deleteChannel(int channelIndex);
void loadRxChannelSettings(const Preset* preset, PluginAPI *pluginAPI);
void saveRxChannelSettings(Preset* preset);
void loadTxChannelSettings(const Preset* preset, PluginAPI *pluginAPI);
void saveTxChannelSettings(Preset* preset);
void loadMIMOChannelSettings(const Preset* preset, PluginAPI *pluginAPI);
void saveMIMOChannelSettings(Preset* preset);
void addRxChannel(int selectedChannelIndex, PluginAPI *pluginAPI);
void addTxChannel(int selectedChannelIndex, PluginAPI *pluginAPI);
void addMIMOChannel(int selectedChannelIndex, PluginAPI *pluginAPI);
// REST API
int webapiSpectrumSettingsGet(SWGSDRangel::SWGGLSpectrum& response, QString& errorMessage) const;