diff --git a/tools/plugingenerator/config.json b/tools/plugingenerator/config.json
index 3ba3c4c9ca..78f36c2d89 100644
--- a/tools/plugingenerator/config.json
+++ b/tools/plugingenerator/config.json
@@ -1,4 +1,9 @@
{
+ "pdk":{
+ "enable": true,
+ "deps_path": "",
+ "project_path": ""
+ },
"plugin": {
"dir_name": "new",
"plugin_name": "newplugin",
diff --git a/tools/plugingenerator/plugin_generator.py b/tools/plugingenerator/plugin_generator.py
index 7e176c0d54..b1452e809c 100644
--- a/tools/plugingenerator/plugin_generator.py
+++ b/tools/plugingenerator/plugin_generator.py
@@ -25,11 +25,6 @@
pathToScopy = args.scopy_path
else:
pathToScopy = os.path.dirname(os.path.dirname(os.getcwd()))
-
-pluginsPath = os.path.join(pathToScopy, "plugins")
-if not os.path.exists(pluginsPath):
- print("Couldn't find " + pluginsPath + " path!")
- exit(1)
pathToConfigFile = ""
if args.config_file_path:
@@ -49,6 +44,7 @@
filesGenerated = []
directoriesGenerated = []
+pdkSupport = generatorOptions["pdk"]["enable"]
pluginDirName = generatorOptions["plugin"]["dir_name"]
pluginName = generatorOptions["plugin"]["plugin_name"]
pluginDisplayName = generatorOptions["plugin"]["plugin_display_name"]
@@ -57,6 +53,32 @@
pluginExportMacro = "SCOPY_" + pluginName.upper() + "_EXPORT"
print("Starting file generation:")
+
+pluginsPath = os.path.join(pathToScopy, "plugins")
+if pdkSupport:
+ pdkPath = generatorOptions["pdk"]["project_path"]
+ if not pdkPath:
+ pdkPath = os.path.join(pathToScopy, "ScopyPluginRunner")
+ else:
+ pdkPath = os.path.join(pdkPath, "ScopyPluginRunner")
+ try:
+ os.makedirs(pdkPath, mode)
+ directoriesGenerated.append(pdkPath)
+ except FileExistsError:
+ print(pdkPath + " directory already exists!")
+
+ pluginsPath = os.path.join(pdkPath, "plugin")
+ try:
+ os.mkdir(pluginsPath, mode)
+ directoriesGenerated.append(pluginsPath)
+ except FileExistsError:
+ print(pluginsPath + " directory already exists!")
+
+
+if not os.path.exists(pluginsPath):
+ print("Couldn't find " + pluginsPath + " path!")
+ exit(1)
+
#################################################### Plugin dir #############################################
newPluginPath = os.path.join(pluginsPath, pluginDirName)
if not os.path.exists(newPluginPath):
@@ -98,6 +120,127 @@
else:
print(pluginSrcConfigPath + " file already exists!")
+####################################################### pdk ##################################################
+if pdkSupport:
+ scopyPdkDeps = generatorOptions["pdk"]["deps_path"]
+ pdkPreferencesPath = os.path.join(pdkPath, "preferences.ini")
+ if not os.path.exists(pdkPreferencesPath):
+ pdkPreferences = open(pdkPreferencesPath, "w")
+ pdkPreferences.close()
+
+ pdkCmakeFuncFilePath = os.path.join(pdkPath, "PdkSupport.cmake")
+ if not os.path.exists(pdkCmakeFuncFilePath):
+ pdkCmakeFuncTemplate = Template(
+ filename="templates/pdk/pdk_cmake_func_template.mako"
+ )
+ pdkCmakeFuncContent = pdkCmakeFuncTemplate.render()
+ pdkCmakeFuncFile = open(pdkCmakeFuncFilePath, "w")
+ pdkCmakeFuncFile.write(pdkCmakeFuncContent)
+ pdkCmakeFuncFile.close()
+ filesGenerated.append(pdkCmakeFuncFilePath)
+ else:
+ print(pdkCmakeFuncFilePath + " file already exists!")
+
+ pdkCmakeFilePath = os.path.join(pdkPath, "CMakeLists.txt")
+ if not os.path.exists(pdkCmakeFilePath):
+ pdkCmakeTemplate = Template(
+ filename="templates/pdk/pdk_cmake_template.mako"
+ )
+ pdkCmakeContent = pdkCmakeTemplate.render(
+ deps_path=scopyPdkDeps,
+ plugin_dir=pluginDirName,
+ plugin_name=pluginName,
+ preferences_path = pdkPreferencesPath
+ )
+ pdkCmakeFile = open(pdkCmakeFilePath, "w")
+ pdkCmakeFile.write(pdkCmakeContent)
+ pdkCmakeFile.close()
+ filesGenerated.append(pdkCmakeFilePath)
+ else:
+ print(pdkCmakeFilePath + " file already exists!")
+
+ pdkIncludePath = os.path.join(pdkPath, "include")
+ try:
+ os.mkdir(pdkIncludePath, mode)
+ directoriesGenerated.append(pdkIncludePath)
+ except FileExistsError:
+ print(pdkIncludePath + " directory already exists!")
+
+ pdkHeaderFilePath = os.path.join(pdkIncludePath, "pdkwindow.h")
+ if not os.path.exists(pdkHeaderFilePath):
+ pdkHeaderTemplate = Template(
+ filename="templates/pdk/pdk_header_template.mako"
+ )
+ pdkHeaderContent = pdkHeaderTemplate.render()
+ pdkHeaderFile = open(pdkHeaderFilePath, "w")
+ pdkHeaderFile.write(pdkHeaderContent)
+ pdkHeaderFile.close()
+ filesGenerated.append(pdkHeaderFilePath)
+ else:
+ print(pdkHeaderFilePath + " file already exists!")
+
+ pdkCmakeInFilePath = os.path.join(pdkIncludePath, "pdk-util_config.h.cmakein")
+ if not os.path.exists(pdkCmakeInFilePath):
+ pdkCmakeInTemplate = Template(
+ filename="templates/pdk/pdk_cmakein_template.mako"
+ )
+ pdkCmakeInTemplate = pdkCmakeInTemplate.render()
+ pdkCmakeInFile = open(pdkCmakeInFilePath, "w")
+ pdkCmakeInFile.write(pdkCmakeInTemplate)
+ pdkCmakeInFile.close()
+ filesGenerated.append(pdkCmakeInFilePath)
+ else:
+ print(pdkCmakeInFilePath + " file already exists!")
+
+ pdkSrcPath = os.path.join(pdkPath, "src")
+ try:
+ os.mkdir(pdkSrcPath, mode)
+ directoriesGenerated.append(pdkSrcPath)
+ except FileExistsError:
+ print(pdkSrcPath + " directory already exists!")
+
+ pdkSrcFilePath = os.path.join(pdkSrcPath, "main.cpp")
+ if not os.path.exists(pdkSrcFilePath):
+ pdkSrcTemplate = Template(
+ filename="templates/pdk/pdk_src_template.mako"
+ )
+ pdkSrcContent = pdkSrcTemplate.render()
+ pdkSrcFile = open(pdkSrcFilePath, "w")
+ pdkSrcFile.write(pdkSrcContent)
+ pdkSrcFile.close()
+ filesGenerated.append(pdkSrcFilePath)
+
+ pdkResPath = os.path.join(pdkPath, "res")
+ try:
+ os.mkdir(pdkResPath, mode)
+ directoriesGenerated.append(pdkResPath)
+ except FileExistsError:
+ print(pdkResPath + " directory already exists!")
+
+ qssPdkPath = os.path.join(pdkResPath,"default.qss")
+ defaultQssPath = os.path.join(scopyPdkDeps,"usr/local/resources/stylesheets/default.qss")
+ if not os.path.exists(defaultQssPath):
+ defaultQssPath = os.path.join(pathToScopy,"gui/res/stylesheets/default.qss")
+
+ if os.path.exists(defaultQssPath):
+ if not os.path.exists(qssPdkPath):
+ shutil.copy(defaultQssPath,pdkResPath)
+ else:
+ print(qssPdkPath + " file already exists!")
+ else:
+ print("default.qss stylesheet couldn't be found!")
+ pdkResQrc = os.path.join(pdkResPath, "resources.qrc")
+ if not os.path.exists(pdkResQrc):
+ resFile = open(pdkResQrc, "w")
+ resFile.write("\n")
+ resFile.write(" \n")
+ if os.path.exists(qssPdkPath):
+ resFile.write(" default.qss\n")
+ resFile.write(" \n")
+ resFile.write("")
+ resFile.close()
+ filesGenerated.append(pdkResQrc)
+
##################################################### Include ################################################
includePath = os.path.join(newPluginPath, "include")
try:
@@ -171,8 +314,9 @@
+ str(generatorOptions["test"]["cmake_min_required"])
+ ")\n\n"
)
- if generatorOptions["test"]["tst_pluginloader"]:
- testCmakeFile.write("include(ScopyTest)\n\nsetup_scopy_tests(pluginloader)")
+ if not pdkSupport:
+ if generatorOptions["test"]["tst_pluginloader"]:
+ testCmakeFile.write("include(ScopyTest)\n\nsetup_scopy_tests(pluginloader)")
filesGenerated.append(testCmakePath)
else:
print(testCmakePath + " file already exists!")
@@ -214,13 +358,16 @@
print(resQrc + " file already exists!")
##################################################### Plugin CMakeLists #########################################
+
if generatorOptions["plugin"]["cmakelists"]:
cmakeListsPath = os.path.join(newPluginPath, "CMakeLists.txt")
cmakeTemplate = Template(filename="templates/cmakelists_template.mako")
+
cmakeContent = cmakeTemplate.render(
- scopy_module=pluginName,
- plugin_display_name=pluginDisplayName,
- plugin_description=pluginDecription, config=generatorOptions["cmakelists"]
+ pdk_en=pdkSupport,
+ scopy_module=pluginName,
+ plugin_display_name=pluginDisplayName,
+ plugin_description=pluginDecription, config=generatorOptions["cmakelists"]
)
if not os.path.exists(cmakeListsPath):
@@ -247,7 +394,7 @@
filesGenerated.append(cmakeinFilePath)
else:
- print(cmakeinFilePath + " file already exists!")
+ print(cmakeinFilePath + " file already exists!")
##################################################### Plugin ToolList #########################################
diff --git a/tools/plugingenerator/templates/cmakelists_template.mako b/tools/plugingenerator/templates/cmakelists_template.mako
index dede1117ef..306fbe03a1 100644
--- a/tools/plugingenerator/templates/cmakelists_template.mako
+++ b/tools/plugingenerator/templates/cmakelists_template.mako
@@ -65,6 +65,17 @@ configure_file(
target_include_directories(${"${PROJECT_NAME}"} INTERFACE ${"${CMAKE_CURRENT_SOURCE_DIR}"}/include)
target_include_directories(${"${PROJECT_NAME}"} PRIVATE ${"${CMAKE_CURRENT_SOURCE_DIR}"}/include/${"${SCOPY_MODULE}"})
+% if pdk_en:
+target_include_directories(${"${PROJECT_NAME}"} PRIVATE ${"${PDK_DEPS_INCLUDE}"})
+
+include(${"${CMAKE_SOURCE_DIR}"}/PdkSupport.cmake)
+inlcude_dirs(${"${PDK_DEPS_INCLUDE}"})
+
+target_link_libraries(${"${PROJECT_NAME}"} PUBLIC Qt::Widgets Qt::Core)
+
+link_libs(${"${PDK_DEPS_LIB}"})
+
+% else:
target_include_directories(${"${PROJECT_NAME}"} PUBLIC scopy-pluginbase scopy-gui)
target_link_libraries(
@@ -76,6 +87,8 @@ target_link_libraries(
scopy-iioutil
)
+% endif
+
if(${"${CMAKE_SYSTEM_NAME}"} MATCHES "Windows")
configureinstallersettings(${"${SCOPY_MODULE}"} ${"${PLUGIN_DESCRIPTION}"} FALSE)
endif()
diff --git a/tools/plugingenerator/templates/pdk/pdk_cmake_func_template.mako b/tools/plugingenerator/templates/pdk/pdk_cmake_func_template.mako
new file mode 100644
index 0000000000..1ffb4998c5
--- /dev/null
+++ b/tools/plugingenerator/templates/pdk/pdk_cmake_func_template.mako
@@ -0,0 +1,25 @@
+# Define a function to include all directories recursively
+function(inlcude_dirs root_dir)
+ # Find all files and directories recursively
+ file(GLOB_RECURSE all_items LIST_DIRECTORIES true ""${"${root_dir}"}/"")
+ # Loop through each item found
+ foreach(item ${"${all_items}"})
+ # Check if the item is a directory
+ if(IS_DIRECTORY ${"${item}"})
+ message(${"${item}"})
+ target_include_directories(${"${PROJECT_NAME}"} PRIVATE ${"${item}"})
+ endif()
+ endforeach()
+endfunction()
+
+# Define a function to link all .so files from a root directory
+function(link_libs root_dir)
+ # Find all .so files from root_dir
+ file(GLOB all_libs "${"${root_dir}"}/*.so")
+ # Loop through each library found
+ foreach(lib ${"${all_libs}"})
+ # Link libraries
+ message(${"${lib}"})
+ target_link_libraries(${"${PROJECT_NAME}"} PRIVATE ${"${lib}"})
+ endforeach()
+endfunction()
\ No newline at end of file
diff --git a/tools/plugingenerator/templates/pdk/pdk_cmake_template.mako b/tools/plugingenerator/templates/pdk/pdk_cmake_template.mako
new file mode 100644
index 0000000000..d5feba4bd5
--- /dev/null
+++ b/tools/plugingenerator/templates/pdk/pdk_cmake_template.mako
@@ -0,0 +1,66 @@
+cmake_minimum_required(VERSION 3.9)
+
+# Project name
+set(TARGET_NAME "ScopyPluginRunner")
+
+project(${"${TARGET_NAME}"} VERSION 0.0.1 DESCRIPTION "Project Description")
+
+# Make sure CMake will take care of moc for us
+set(CMAKE_AUTOMOC ON)
+
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(PDK_DEPS_PATH ${deps_path})
+if(NOT DEFINED PDK_DEPS_PATH)
+ message(FATAL_ERROR "PDK_DEPS_PATH is required!")
+else()
+ if(NOT EXISTS ${"${PDK_DEPS_PATH}"})
+ message(FATAL_ERROR "The path=" \"${"${PDK_DEPS_PATH}"}\" " to the dependencies doesn't exist!")
+ endif()
+endif()
+set(PDK_DEPS_INCLUDE ${"${PDK_DEPS_PATH}"}/usr/local/include)
+if(NOT EXISTS ${"${PDK_DEPS_INCLUDE}"})
+ message(FATAL_ERROR "The path=" \"${"${PDK_DEPS_INCLUDE}"}\" " to the headers doesn't exist!")
+endif()
+
+set(PDK_DEPS_LIB ${"${PDK_DEPS_PATH}"}/usr/local/lib)
+if(NOT EXISTS ${"${PDK_DEPS_LIB}"})
+ message(FATAL_ERROR "The path=" \"${"${PDK_DEPS_LIB}"}\" " to the libraries doesn't exist!")
+endif()
+
+set(PLUGIN_INSTALL_PATH ${"${CMAKE_CURRENT_BINARY_DIR}"}/plugin/${plugin_dir}/libscopy-${plugin_name}.so)
+set(PATH_TO_INI ${preferences_path})
+
+find_package(QT NAMES Qt5 REQUIRED COMPONENTS Widgets)
+find_package(Qt${"${QT_VERSION_MAJOR}"} REQUIRED COMPONENTS Widgets Core)
+
+file(GLOB SRC_LIST src/*.cpp)
+file(GLOB HEADER_LIST include/*.h include/*.hpp)
+
+configure_file(include/pdk-util_config.h.cmakein ${"${CMAKE_CURRENT_SOURCE_DIR}"}/include/pdk-util_config.h @ONLY)
+
+set(PROJECT_SOURCES ${"${SRC_LIST}"} ${"${HEADER_LIST}"} ${"${CMAKE_CURRENT_SOURCE_DIR}"}/include/pdk-util_config.h)
+find_path(IIO_INCLUDE_DIRS iio.h REQUIRED)
+find_library(IIO_LIBRARIES NAMES iio libiio REQUIRED)
+
+add_subdirectory(plugin/${plugin_dir})
+
+qt_add_resources(PROJ_RES res/resources.qrc)
+
+add_executable(${"${TARGET_NAME}"} ${"${PROJECT_SOURCES}"} ${"${PROJ_RES}"})
+
+include(${"${CMAKE_CURRENT_SOURCE_DIR}"}/PdkSupport.cmake)
+
+target_include_directories(${"${TARGET_NAME}"} PRIVATE ${"${Qt${QT_VERSION_MAJOR}_INCLUDE_DIRS}"})
+target_include_directories(${"${TARGET_NAME}"} PRIVATE ${"${CMAKE_SOURCE_DIR}"}/include)
+target_include_directories(${"${TARGET_NAME}"} INTERFACE ${"${IIO_INCLUDE_DIRS}"})
+
+target_include_directories(${"${TARGET_NAME}"} PUBLIC ${"${PDK_DEPS_INCLUDE}"} ${"${IIO_INCLUDE_DIRS}"})
+
+inlcude_dirs(${"${PDK_DEPS_INCLUDE}"})
+# Add any extra libs to link also.
+link_libs(${"${PDK_DEPS_LIB}"})
+target_link_libraries(
+ ${"${TARGET_NAME}"} PRIVATE Qt${"${QT_VERSION_MAJOR}"}::Widgets Qt${"${QT_VERSION_MAJOR}"}::Core ${"${IIO_LIBRARIES}"}
+)
diff --git a/tools/plugingenerator/templates/pdk/pdk_cmakein_template.mako b/tools/plugingenerator/templates/pdk/pdk_cmakein_template.mako
new file mode 100644
index 0000000000..dd26174d7c
--- /dev/null
+++ b/tools/plugingenerator/templates/pdk/pdk_cmakein_template.mako
@@ -0,0 +1,7 @@
+#ifndef PDK_UTIL_H_CMAKEIN
+#define PDK_UTIL_H_CMAKEIN
+
+#define PLUGIN_INSTALL_PATH "@PLUGIN_INSTALL_PATH@"
+#define PATH_TO_INI "@PATH_TO_INI@"
+
+#endif // PDK_UTIL_H_CMAKEIN
diff --git a/tools/plugingenerator/templates/pdk/pdk_header_template.mako b/tools/plugingenerator/templates/pdk/pdk_header_template.mako
new file mode 100644
index 0000000000..d033dd16ee
--- /dev/null
+++ b/tools/plugingenerator/templates/pdk/pdk_header_template.mako
@@ -0,0 +1,146 @@
+#include "plugin.h"
+#include "toolmenuentry.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+class MainWidget;
+class PluginManager;
+
+class PDKWindow : public QMainWindow
+{
+ Q_OBJECT
+public:
+ PDKWindow(QWidget *parent = nullptr);
+ ~PDKWindow();
+
+public Q_SLOTS:
+ void onConnect();
+ void onDisconnect();
+ void onInit();
+ void onDeinit();
+ void onLoad();
+ void onUnload();
+Q_SIGNALS:
+ void sigLabelTextUpdated(std::string_view);
+
+private:
+ QTabWidget *m_tabWidget;
+ QWidget *m_prefPage;
+ QList m_toolList;
+ QList m_loadedTools;
+ QLabel *m_aboutPage = nullptr;
+ MainWidget *m_mainWidget = nullptr;
+
+ void initMainWindow();
+ void updateLabelText();
+ void initPreferencesPage();
+ void addPluginPrefPage();
+ void removePluginPrefPage();
+ QLabel *createTabLabel(QString name);
+ QWidget *addHorizontalTab(QWidget *w, QLabel *lbl, bool tabEnabled = true);
+ QWidget *buildSaveSessionPreference();
+ QWidget *generalPreferences();
+ void initGeneralPreferences();
+};
+
+class MainWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ MainWidget(QWidget *parent);
+ ~MainWidget();
+
+ QString pluginAbout();
+ QWidget *pluginPrefPage();
+ QList getPluginTools();
+
+Q_SIGNALS:
+ void connected();
+ void disconnected();
+ void init();
+ void deinit();
+ void loaded();
+ void unloaded();
+
+private Q_SLOTS:
+ void onConnect();
+ void onDisconnect();
+ void deinitialize();
+ void onInit();
+ void onLoad();
+ void onUnload();
+ void browseFile(QLineEdit *pluginPathEdit);
+ void deletePluginManager();
+
+private:
+ void unloadInfoPage();
+ void loadInfoPage();
+ void changeVisibility(QPushButton *btn1, QPushButton *btn2);
+ void enableBrowseInit(bool en);
+ void enableLoadConn(bool en);
+ void updateStatusLbl(QString msg);
+ void loadAvailCat();
+ bool validConnection(QString uri, QString cat);
+ bool isCompatible(QString uri, QString cat);
+
+ PluginManager *m_pluginManager;
+ QLabel *m_statusLbl;
+ QLineEdit *m_uriEdit;
+ QLineEdit *m_pluginPathEdit;
+ QPushButton *m_browseBtn;
+ QPushButton *m_initBtn;
+ QPushButton *m_loadBtn;
+ QPushButton *m_unloadBtn;
+ QPushButton *m_connBtn;
+ QPushButton *m_disconnBtn;
+
+ QWidget *m_pluginIcon;
+ scopy::MenuCombo *m_deviceTypeCb;
+ QScrollArea *m_scrollArea;
+ QString m_currentPlugin;
+ enum State
+ {
+ Unloaded,
+ Loaded,
+ Connected
+ } m_state;
+};
+
+class PluginManager : public QObject
+{
+ Q_OBJECT
+public:
+ PluginManager(QString pluginPath, QObject *parent = nullptr);
+ ~PluginManager();
+
+ scopy::Plugin *plugin() const;
+ bool pluginCompatibility(QString param, QString category);
+
+Q_SIGNALS:
+ void requestTool(QString toolId);
+
+private:
+ void initPlugin();
+ void loadPlugin(QString file);
+
+ scopy::Plugin *m_plugin;
+ bool m_loaded;
+};
+
+class ConnectionStrategy : public QObject
+{
+ Q_OBJECT
+public:
+ ConnectionStrategy(QString uri, QObject *parent = nullptr);
+ ~ConnectionStrategy();
+ bool validConnection(QString cat);
+
+private:
+ bool iioConn();
+ bool testConn();
+ QString m_uri;
+};
diff --git a/tools/plugingenerator/templates/pdk/pdk_src_template.mako b/tools/plugingenerator/templates/pdk/pdk_src_template.mako
new file mode 100644
index 0000000000..b5a570bf70
--- /dev/null
+++ b/tools/plugingenerator/templates/pdk/pdk_src_template.mako
@@ -0,0 +1,820 @@
+#include "pdkwindow.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "gui/stylehelper.h"
+#include "gui/preferenceshelper.h"
+#include "pdk-util_config.h"
+#include
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ QCoreApplication::setOrganizationName("ADI");
+ QCoreApplication::setOrganizationDomain("analog.com");
+ QCoreApplication::setApplicationName("Scopy-PDK");
+ QSettings::setDefaultFormat(QSettings::IniFormat);
+
+ QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
+ app.setStyleSheet(Util::loadStylesheetFromFile(":/default.qss"));
+ PDKWindow test;
+ test.show();
+ int ret = app.exec();
+ if(ret == 0) {
+ qInfo() << "PDK support finished successfully!";
+ }
+ return ret;
+}
+
+PDKWindow::PDKWindow(QWidget *parent)
+ : QMainWindow(parent)
+{
+ setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
+ setMinimumSize(1280, 720);
+ layout()->setMargin(9);
+ layout()->setSpacing(6);
+ scopy::StyleHelper::GetInstance()->initColorMap();
+ m_mainWidget = new MainWidget(this);
+ m_aboutPage = new QLabel(this);
+ m_aboutPage->setWordWrap(true);
+ m_aboutPage->setAlignment(Qt::AlignTop);
+ initMainWindow();
+ initPreferencesPage();
+
+ addHorizontalTab(m_mainWidget, createTabLabel("Main"));
+ addHorizontalTab(m_aboutPage, createTabLabel("About"));
+ addHorizontalTab(m_prefPage, createTabLabel("Preferences"));
+ connect(m_mainWidget, &MainWidget::connected, this, &PDKWindow::onConnect);
+ connect(m_mainWidget, &MainWidget::disconnected, this, &PDKWindow::onDisconnect);
+ connect(m_mainWidget, &MainWidget::init, this, &PDKWindow::onInit);
+ connect(m_mainWidget, &MainWidget::deinit, this, &PDKWindow::onDeinit);
+ connect(m_mainWidget, &MainWidget::loaded, this, &PDKWindow::onLoad);
+ connect(m_mainWidget, &MainWidget::unloaded, this, &PDKWindow::onUnload);
+}
+
+PDKWindow::~PDKWindow()
+{
+ if(m_mainWidget) {
+ delete m_mainWidget;
+ m_mainWidget = nullptr;
+ }
+}
+
+void PDKWindow::onConnect()
+{
+ if(!m_loadedTools.isEmpty()) {
+ onUnload();
+ }
+ QList tools = m_mainWidget->getPluginTools();
+ for(const scopy::ToolMenuEntry *t : qAsConst(tools)) {
+ if(!t->tool())
+ continue;
+ QLabel *lbl = createTabLabel(t->name());
+ QWidget *tab = addHorizontalTab(t->tool(), lbl);
+ connect(t, &scopy::ToolMenuEntry::updateToolEntry, this, [lbl, t]() {
+ if(lbl->text().compare(t->name()) != 0) {
+ lbl->setText(t->name());
+ }
+ });
+ m_toolList.append(tab);
+ }
+}
+
+void PDKWindow::onDisconnect()
+{
+ QList tools = m_mainWidget->getPluginTools();
+ for(const scopy::ToolMenuEntry *t : qAsConst(tools)) {
+ t->disconnect(this);
+ }
+ for(auto t : qAsConst(m_toolList)) {
+ int idx = m_tabWidget->indexOf(t);
+ m_tabWidget->removeTab(idx);
+ m_toolList.removeOne(t);
+ }
+ onLoad();
+}
+
+void PDKWindow::onInit()
+{
+ addPluginPrefPage();
+ m_aboutPage->clear();
+ m_aboutPage->setText(m_mainWidget->pluginAbout());
+}
+
+void PDKWindow::onDeinit()
+{
+ m_aboutPage->clear();
+ removePluginPrefPage();
+}
+
+void PDKWindow::onLoad()
+{
+ QList tools = m_mainWidget->getPluginTools();
+ for(const scopy::ToolMenuEntry *t : qAsConst(tools)) {
+ if(t->name().isEmpty())
+ continue;
+ QLabel *lbl = createTabLabel(t->name());
+ QWidget *tab = addHorizontalTab(new QWidget(), lbl, false);
+ m_loadedTools.append(tab);
+ }
+}
+
+void PDKWindow::onUnload()
+{
+ if(m_loadedTools.isEmpty())
+ return;
+ for(auto t : qAsConst(m_loadedTools)) {
+ int idx = m_tabWidget->indexOf(t);
+ m_tabWidget->removeTab(idx);
+ m_loadedTools.removeOne(t);
+ }
+}
+
+QWidget *PDKWindow::buildSaveSessionPreference()
+{
+ scopy::Preferences *p = scopy::Preferences::GetInstance();
+ QWidget *w = new QWidget(this);
+ QHBoxLayout *lay = new QHBoxLayout(w);
+ lay->setMargin(0);
+
+ lay->addWidget(scopy::PreferencesHelper::addPreferenceCheckBox(p, "general_save_session",
+ "Save/Load Scopy session", this));
+ lay->addSpacerItem(new QSpacerItem(40, 40, QSizePolicy::Expanding, QSizePolicy::Fixed));
+ lay->addWidget(new QLabel("Settings files location ", this));
+ QPushButton *navigateBtn = new QPushButton("Open", this);
+ scopy::StyleHelper::BlueButton(navigateBtn, "navigateBtn");
+ navigateBtn->setMaximumWidth(80);
+ connect(navigateBtn, &QPushButton::clicked, this,
+ [=]() { QDesktopServices::openUrl(scopy::config::settingsFolderPath()); });
+ lay->addWidget(navigateBtn);
+ return w;
+}
+
+QWidget *PDKWindow::generalPreferences()
+{
+ QWidget *page = new QWidget(this);
+ QVBoxLayout *lay = new QVBoxLayout(page);
+ initGeneralPreferences();
+ scopy::Preferences *p = scopy::Preferences::GetInstance();
+
+ lay->setMargin(0);
+ lay->setSpacing(10);
+ page->setLayout(lay);
+
+ // General preferences
+ scopy::MenuSectionWidget *generalWidget = new scopy::MenuSectionWidget(page);
+ scopy::MenuCollapseSection *generalSection =
+ new scopy::MenuCollapseSection("General", scopy::MenuCollapseSection::MHCW_NONE, generalWidget);
+ generalWidget->contentLayout()->setSpacing(10);
+ generalWidget->contentLayout()->addWidget(generalSection);
+ generalSection->contentLayout()->setSpacing(10);
+ lay->addWidget(generalWidget);
+ lay->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding));
+
+ generalSection->contentLayout()->addWidget(buildSaveSessionPreference());
+ generalSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCheckBox(
+ p, "general_save_attached", "Save/Load tool attached state", generalSection));
+ generalSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCheckBox(
+ p, "general_doubleclick_attach", "Doubleclick to attach/detach tool", generalSection));
+ generalSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCheckBox(
+ p, "general_doubleclick_ctrl_opens_menu", "Doubleclick control buttons to open menu", generalSection));
+ generalSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCheckBox(
+ p, "general_use_opengl", "Enable OpenGL plotting", generalSection));
+ generalSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCheckBox(
+ p, "general_use_animations", "Enable menu animations", generalSection));
+ generalSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCheckBox(
+ p, "general_check_online_version", "Enable automatic online check for updates.", generalSection));
+ generalSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCheckBox(
+ p, "general_show_status_bar", "Enable the status bar for displaying important messages.",
+ generalSection));
+ generalSection->contentLayout()->addWidget(
+ scopy::PreferencesHelper::addPreferenceCheckBox(p, "show_grid", "Show Grid", generalSection));
+ generalSection->contentLayout()->addWidget(
+ scopy::PreferencesHelper::addPreferenceCheckBox(p, "show_graticule", "Show Graticule", generalSection));
+ generalSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCheckBox(
+ p, "iiowidgets_use_lazy_loading", "Use Lazy Loading", generalSection));
+ generalSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCheckBox(
+ p, "plugins_use_debugger_v2", "Use Debugger V2 plugin", generalSection));
+ generalSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCombo(
+ p, "general_theme", "Theme", {"default", "light"}, generalSection));
+
+ // Debug preferences
+ scopy::MenuSectionWidget *debugWidget = new scopy::MenuSectionWidget(page);
+ scopy::MenuCollapseSection *debugSection =
+ new scopy::MenuCollapseSection("Debug", scopy::MenuCollapseSection::MHCW_NONE, debugWidget);
+ debugWidget->contentLayout()->setSpacing(10);
+ debugWidget->contentLayout()->addWidget(debugSection);
+ debugSection->contentLayout()->setSpacing(10);
+ lay->addWidget(debugWidget);
+ lay->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding));
+
+ debugSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCheckBox(
+ p, "general_show_plot_fps", "Show plot FPS", debugSection));
+ debugSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCombo(
+ p, "general_plot_target_fps", "Plot target FPS", {"15", "20", "30", "60"}, debugSection));
+
+ return page;
+}
+
+void PDKWindow::initGeneralPreferences()
+{
+ scopy::Preferences *p = scopy::Preferences::GetInstance();
+ p->setPreferencesFilename(PATH_TO_INI);
+ p->load();
+ p->init("general_first_run", true);
+ p->init("general_save_session", true);
+ p->init("general_save_attached", true);
+ p->init("general_doubleclick_attach", true);
+#if defined(__arm__)
+ p->init("general_use_opengl", false);
+#else
+ p->init("general_use_opengl", true);
+#endif
+ p->init("general_use_animations", true);
+ p->init("general_theme", "default");
+ p->init("general_language", "en");
+ p->init("show_grid", true);
+ p->init("show_graticule", false);
+ p->init("iiowidgets_use_lazy_loading", true);
+ p->init("plugins_use_debugger_v2", true);
+ p->init("general_plot_target_fps", "60");
+ p->init("general_show_plot_fps", true);
+ p->init("general_use_native_dialogs", true);
+ p->init("general_additional_plugin_path", "");
+ p->init("general_load_decoders", true);
+ p->init("general_doubleclick_ctrl_opens_menu", true);
+ p->init("general_check_online_version", false);
+ p->init("general_show_status_bar", true);
+}
+
+void PDKWindow::initMainWindow()
+{
+ QWidget *centralWidget = new QWidget(this);
+ centralWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
+ centralWidget->setMinimumSize(1280, 720);
+ setCentralWidget(centralWidget);
+ QHBoxLayout *lay = new QHBoxLayout(centralWidget);
+ lay->setMargin(9);
+ lay->setSpacing(6);
+
+ m_tabWidget = new QTabWidget(centralWidget);
+ m_tabWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
+ m_tabWidget->setTabPosition(QTabWidget::TabPosition::West);
+ scopy::StyleHelper::BackgroundPage(m_tabWidget, "pdkTable");
+
+ lay->addWidget(m_tabWidget);
+}
+
+QWidget *PDKWindow::addHorizontalTab(QWidget *w, QLabel *lbl, bool tabEnabled)
+{
+ QWidget *pane = new QWidget(m_tabWidget);
+ pane->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
+ QHBoxLayout *lay = new QHBoxLayout(pane);
+ lay->setMargin(10);
+ pane->setLayout(lay);
+
+ QScrollArea *scrollArea = new QScrollArea(pane);
+ scrollArea->setWidget(w);
+ scrollArea->setWidgetResizable(true);
+ scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ lay->addWidget(scrollArea);
+
+ // Hackish - so we don't override paint event
+ int tabIdx = m_tabWidget->addTab(pane, "");
+ m_tabWidget->setTabEnabled(tabIdx, tabEnabled);
+
+ QTabBar *tabbar = m_tabWidget->tabBar();
+ tabbar->setTabButton(tabbar->count() - 1, QTabBar::LeftSide, lbl);
+ tabbar->tabButton(tabbar->count() - 1, QTabBar::LeftSide)->setEnabled(tabEnabled);
+ return pane;
+}
+void PDKWindow::initPreferencesPage()
+{
+ m_prefPage = new QWidget(this);
+ QGridLayout *lay = new QGridLayout(m_prefPage);
+ lay->setSpacing(6);
+ lay->setMargin(0);
+
+ scopy::MenuSectionWidget *generalWidget = new scopy::MenuSectionWidget(m_prefPage);
+ scopy::MenuCollapseSection *generalSection = new scopy::MenuCollapseSection(
+ "Scopy preferences", scopy::MenuCollapseSection::MHCW_NONE, generalWidget);
+ generalWidget->contentLayout()->setSpacing(10);
+ generalWidget->contentLayout()->addWidget(generalSection);
+ generalSection->contentLayout()->setSpacing(10);
+ generalSection->contentLayout()->addWidget(generalPreferences());
+
+ lay->addWidget(generalWidget, 0, 0);
+ lay->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding), 2, 0);
+}
+
+void PDKWindow::addPluginPrefPage()
+{
+ QWidget *pluginPref = m_mainWidget->pluginPrefPage();
+ if(pluginPref) {
+ QGridLayout *lay = dynamic_cast(m_prefPage->layout());
+ if(!lay)
+ return;
+ scopy::MenuSectionWidget *pluginWidget = new scopy::MenuSectionWidget(m_prefPage);
+ scopy::MenuCollapseSection *pluginSection = new scopy::MenuCollapseSection(
+ "Plugin preferences", scopy::MenuCollapseSection::MHCW_NONE, pluginWidget);
+ pluginWidget->contentLayout()->setSpacing(10);
+ pluginWidget->contentLayout()->addWidget(pluginSection);
+ pluginSection->contentLayout()->setSpacing(10);
+ pluginSection->contentLayout()->addWidget(pluginPref);
+ lay->addWidget(pluginWidget, 1, 0);
+ }
+}
+
+void PDKWindow::removePluginPrefPage()
+{
+ QGridLayout *lay = dynamic_cast(m_prefPage->layout());
+ QLayoutItem *it = lay->itemAtPosition(1, 0);
+ if(!it) {
+ return;
+ }
+ QWidget *pluginPref = it->widget();
+ if(pluginPref) {
+ m_prefPage->layout()->removeWidget(pluginPref);
+ delete pluginPref;
+ }
+}
+
+QLabel *PDKWindow::createTabLabel(QString name)
+{
+ QLabel *lbl = new QLabel();
+ scopy::StyleHelper::TabWidgetLabel(lbl, "tabWidgetLabel");
+ lbl->setText(name);
+ return lbl;
+}
+
+MainWidget::MainWidget(QWidget *parent)
+ : QWidget(parent)
+ , m_pluginManager(nullptr)
+{
+ m_state = State::Unloaded;
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ this->setMinimumWidth(1024);
+ QGridLayout *lay = new QGridLayout(this);
+ lay->setMargin(9);
+ lay->setSpacing(6);
+
+ m_statusLbl = new QLabel(this);
+ scopy::StyleHelper::WarningLabel(m_statusLbl);
+ m_statusLbl->setWordWrap(true);
+
+ // browse section
+ m_browseBtn = new QPushButton("Browse", this);
+ scopy::StyleHelper::BlueButton(m_browseBtn);
+ m_browseBtn->setFixedWidth(128);
+ m_browseBtn->setFixedHeight(40);
+
+ m_pluginPathEdit = new QLineEdit(this);
+ m_pluginPathEdit->setText(PLUGIN_INSTALL_PATH);
+ m_pluginPathEdit->setPlaceholderText(PLUGIN_INSTALL_PATH);
+ connect(m_browseBtn, &QPushButton::clicked, this, [this]() {
+ browseFile(m_pluginPathEdit);
+ enableLoadConn(false);
+ });
+
+ // init
+ m_initBtn = new QPushButton("Init", this);
+ scopy::StyleHelper::BlueButton(m_initBtn);
+ m_initBtn->setFixedWidth(128);
+ m_initBtn->setFixedHeight(40);
+ connect(m_initBtn, &QPushButton::clicked, this, &MainWidget::onInit);
+
+ // load btn
+ QWidget *loadBtns = new QWidget(this);
+ loadBtns->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
+ loadBtns->setLayout(new QVBoxLayout(loadBtns));
+ loadBtns->layout()->setSpacing(0);
+ loadBtns->layout()->setMargin(0);
+
+ m_loadBtn = new QPushButton("Load", this);
+ scopy::StyleHelper::BlueButton(m_loadBtn);
+ m_loadBtn->setFixedWidth(128);
+ m_loadBtn->setFixedHeight(40);
+ m_loadBtn->setEnabled(false);
+ connect(m_loadBtn, &QPushButton::clicked, this, &MainWidget::onLoad);
+
+ m_unloadBtn = new QPushButton("Unload", this);
+ scopy::StyleHelper::BlueButton(m_unloadBtn);
+ m_unloadBtn->setFixedWidth(128);
+ m_unloadBtn->setFixedHeight(40);
+ m_unloadBtn->setVisible(false);
+ connect(m_unloadBtn, &QPushButton::clicked, this, &MainWidget::onUnload);
+
+ loadBtns->layout()->addWidget(m_loadBtn);
+ loadBtns->layout()->addWidget(m_unloadBtn);
+
+ // connect section
+ m_uriEdit = new QLineEdit(this);
+ m_uriEdit->setPlaceholderText("URI");
+ m_uriEdit->setText("ip:127.0.0.1");
+
+ m_deviceTypeCb = new scopy::MenuCombo("category", this);
+ m_deviceTypeCb->setFixedHeight(40);
+
+ QWidget *connBtns = new QWidget(this);
+ connBtns->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
+ connBtns->setLayout(new QVBoxLayout(connBtns));
+ connBtns->layout()->setSpacing(0);
+ connBtns->layout()->setMargin(0);
+
+ m_connBtn = new QPushButton("Connect", this);
+ scopy::StyleHelper::BlueButton(m_connBtn);
+ m_connBtn->setFixedWidth(128);
+ m_connBtn->setFixedHeight(40);
+ m_connBtn->setEnabled(false);
+ connect(m_connBtn, &QPushButton::clicked, this, &MainWidget::onConnect);
+
+ m_disconnBtn = new QPushButton("Disconnect", this);
+ scopy::StyleHelper::BlueButton(m_disconnBtn);
+ m_disconnBtn->setFixedWidth(128);
+ m_disconnBtn->setFixedHeight(40);
+ m_disconnBtn->setVisible(false);
+ connect(m_disconnBtn, &QPushButton::clicked, this, &MainWidget::onDisconnect);
+
+ connBtns->layout()->addWidget(m_connBtn);
+ connBtns->layout()->addWidget(m_disconnBtn);
+
+ // plugin info
+ QWidget *pluginPage = new QWidget(this);
+ pluginPage->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ pluginPage->setLayout(new QVBoxLayout(pluginPage));
+ pluginPage->layout()->setMargin(0);
+
+ m_scrollArea = new QScrollArea(pluginPage);
+ m_scrollArea->setWidgetResizable(true);
+ m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ pluginPage->layout()->addWidget(m_scrollArea);
+
+ m_pluginIcon = new QWidget(this);
+ m_pluginIcon->setLayout(new QHBoxLayout(m_pluginIcon));
+ m_pluginIcon->layout()->setMargin(0);
+ m_pluginIcon->setFixedSize(100, 100);
+
+ lay->addWidget(m_pluginPathEdit, 0, 0);
+ lay->addWidget(m_deviceTypeCb, 1, 0);
+ lay->addWidget(m_uriEdit, 2, 0);
+ lay->addWidget(m_statusLbl, 3, 0);
+ lay->addWidget(pluginPage, 4, 0);
+
+ lay->addWidget(m_browseBtn, 0, 1, Qt::AlignTop);
+ lay->addWidget(m_initBtn, 1, 1, Qt::AlignTop);
+ lay->addWidget(loadBtns, 2, 1, Qt::AlignTop);
+ lay->addWidget(connBtns, 3, 1, Qt::AlignTop);
+ lay->addWidget(m_pluginIcon, 4, 1, Qt::AlignCenter | Qt::AlignBottom);
+}
+
+MainWidget::~MainWidget()
+{
+ if(m_state == State::Connected) {
+ onDisconnect();
+ }
+ if(m_state == State::Loaded) {
+ onUnload();
+ }
+ if(!m_currentPlugin.isEmpty()) {
+ deinitialize();
+ }
+}
+
+QString MainWidget::pluginAbout()
+{
+ if(!m_pluginManager || !m_pluginManager->plugin())
+ return "";
+ return m_pluginManager->plugin()->about();
+}
+
+QWidget *MainWidget::pluginPrefPage()
+{
+ QWidget *prefPage = nullptr;
+ m_pluginManager->plugin()->initPreferences();
+ if(m_pluginManager->plugin()->loadPreferencesPage())
+ prefPage = m_pluginManager->plugin()->preferencesPage();
+
+ return prefPage;
+}
+
+QList MainWidget::getPluginTools() { return m_pluginManager->plugin()->toolList(); }
+
+void MainWidget::onConnect()
+{
+ if(m_state == State::Unloaded)
+ onLoad();
+ if(m_state != State::Loaded)
+ return;
+ if(!m_pluginManager->plugin()->onConnect()) {
+ updateStatusLbl("Connection failed!");
+ return;
+ }
+ changeVisibility(m_disconnBtn, m_connBtn);
+ enableBrowseInit(false);
+ m_unloadBtn->setEnabled(false);
+ m_state = State::Connected;
+ updateStatusLbl("");
+ Q_EMIT connected();
+}
+
+void MainWidget::onDisconnect()
+{
+ m_pluginManager->plugin()->onDisconnect();
+ changeVisibility(m_disconnBtn, m_connBtn);
+ m_unloadBtn->setEnabled(true);
+ m_state = State::Loaded;
+ Q_EMIT disconnected();
+}
+
+void MainWidget::changeVisibility(QPushButton *btn1, QPushButton *btn2)
+{
+ btn1->setVisible(!btn1->isVisible());
+ btn2->setVisible(!btn2->isVisible());
+}
+
+void MainWidget::enableBrowseInit(bool en)
+{
+ m_initBtn->setEnabled(en);
+ m_browseBtn->setEnabled(en);
+}
+
+void MainWidget::enableLoadConn(bool en)
+{
+ m_loadBtn->setEnabled(en);
+ m_connBtn->setEnabled(en);
+}
+
+void MainWidget::updateStatusLbl(QString msg)
+{
+ m_statusLbl->clear();
+ m_statusLbl->setText(msg);
+}
+
+void MainWidget::loadAvailCat()
+{
+ scopy::Plugin *plugin = m_pluginManager->plugin();
+ if(plugin->metadata().isEmpty())
+ return;
+ if(!plugin->metadata().contains("category")) // plugin metadata does not have category
+ return;
+ QJsonValue categoryVal = plugin->metadata().value("category");
+ if(categoryVal.isString()) // single category
+ m_deviceTypeCb->combo()->addItem(categoryVal.toString());
+ if(categoryVal.isArray()) { // list category
+ QJsonArray catArray = categoryVal.toArray();
+ for(const auto &v : catArray) {
+ if(!v.isString()) {
+ continue;
+ }
+ m_deviceTypeCb->combo()->addItem(v.toString());
+ }
+ }
+}
+
+void MainWidget::deinitialize()
+{
+ if(m_pluginManager) {
+ QWidget *pluginIcon = m_pluginManager->plugin()->icon();
+ if(pluginIcon) {
+ m_pluginIcon->layout()->removeWidget(pluginIcon);
+ delete pluginIcon;
+ }
+ }
+ m_deviceTypeCb->combo()->clear();
+ deletePluginManager();
+ Q_EMIT deinit();
+}
+
+void MainWidget::onInit()
+{
+ QString pluginPath = m_pluginPathEdit->text();
+ if(pluginPath.compare(m_currentPlugin) == 0 && m_pluginManager) {
+ updateStatusLbl("The plugin is already initialized!");
+ return;
+ }
+ deinitialize();
+ m_pluginManager = new PluginManager(pluginPath, this);
+ if(!m_pluginManager->plugin()) {
+ enableLoadConn(false);
+ deletePluginManager();
+ updateStatusLbl("The plugin couldn't be accessed! Path: " + pluginPath);
+ return;
+ }
+ if(m_pluginManager->plugin()->loadIcon()) {
+ m_pluginIcon->layout()->addWidget(m_pluginManager->plugin()->icon());
+ }
+ m_pluginManager->plugin()->initMetadata();
+ loadAvailCat();
+ m_currentPlugin = pluginPath;
+ enableLoadConn(true);
+ updateStatusLbl("");
+ Q_EMIT init();
+}
+
+void MainWidget::onLoad()
+{
+ QString uri = m_uriEdit->text();
+ QString cat = m_deviceTypeCb->combo()->currentText();
+ if(!validConnection(uri, cat) || !isCompatible(uri, cat)) {
+ return;
+ }
+ loadInfoPage();
+ enableBrowseInit(false);
+ changeVisibility(m_unloadBtn, m_loadBtn);
+ m_state = State::Loaded;
+ updateStatusLbl("");
+ m_deviceTypeCb->setEnabled(false);
+ m_uriEdit->setEnabled(false);
+ Q_EMIT loaded();
+}
+
+void MainWidget::onUnload()
+{
+ m_pluginManager->plugin()->unload();
+ unloadInfoPage();
+ enableBrowseInit(true);
+ changeVisibility(m_unloadBtn, m_loadBtn);
+ m_state = State::Unloaded;
+ m_deviceTypeCb->setEnabled(true);
+ m_uriEdit->setEnabled(true);
+ Q_EMIT unloaded();
+}
+
+void MainWidget::browseFile(QLineEdit *pluginPathEdit)
+{
+ QString filePath =
+ QFileDialog::getOpenFileName(this, "Open a file", "directoryToOpen",
+ "All (*);;XML Files (*.xml);;Text Files (*.txt);;BIN Files (*.bin)");
+ pluginPathEdit->setText(filePath);
+}
+
+void MainWidget::deletePluginManager()
+{
+ if(m_pluginManager) {
+ delete m_pluginManager;
+ m_pluginManager = nullptr;
+ }
+}
+
+void MainWidget::unloadInfoPage()
+{
+ QWidget *pluginPage = m_pluginManager->plugin()->page();
+ if(pluginPage) {
+ m_scrollArea->takeWidget();
+ }
+}
+
+void MainWidget::loadInfoPage()
+{
+ if(m_pluginManager->plugin()->loadPage()) {
+ m_scrollArea->setWidget(m_pluginManager->plugin()->page());
+ }
+}
+
+bool MainWidget::validConnection(QString uri, QString cat)
+{
+ ConnectionStrategy *connStrategy = new ConnectionStrategy(uri, this);
+ bool validConn = false;
+ validConn = connStrategy->validConnection(cat);
+ if(!validConn) {
+ updateStatusLbl("Cannot connect to URI!");
+ }
+ connStrategy->deleteLater();
+ return validConn;
+}
+
+bool MainWidget::isCompatible(QString uri, QString cat)
+{
+ bool compatible = false;
+ compatible = m_pluginManager->pluginCompatibility(uri, cat);
+ if(!compatible) {
+ updateStatusLbl("The plugin is not compatible with the device or doesn't exist!");
+ }
+ return compatible;
+}
+
+PluginManager::PluginManager(QString pluginPath, QObject *parent)
+ : QObject(parent)
+ , m_loaded(false)
+ , m_plugin(nullptr)
+{
+ loadPlugin(pluginPath);
+}
+
+PluginManager::~PluginManager()
+{
+ if(m_plugin) {
+ delete m_plugin;
+ m_plugin = nullptr;
+ }
+}
+
+void PluginManager::initPlugin()
+{
+ if(!m_plugin || m_loaded)
+ return;
+ m_plugin->preload();
+ m_plugin->init();
+ m_plugin->loadToolList();
+ m_plugin->loadExtraButtons();
+ m_plugin->postload();
+ m_loaded = true;
+}
+
+bool PluginManager::pluginCompatibility(QString param, QString category)
+{
+ initPlugin();
+ if(!m_plugin->compatible(param, category))
+ return false;
+ m_plugin->setParam(param, category);
+ m_plugin->setEnabled(true);
+ return true;
+}
+
+scopy::Plugin *PluginManager::plugin() const { return m_plugin; }
+
+void PluginManager::loadPlugin(QString file)
+{
+ bool ret;
+ scopy::Plugin *original = nullptr;
+ scopy::Plugin *clone = nullptr;
+
+ if(!QFile::exists(file))
+ return;
+
+ if(!QLibrary::isLibrary(file))
+ return;
+
+ QPluginLoader qp(file);
+ ret = qp.load();
+ if(!ret) {
+ qWarning() << "Cannot load library " + qp.fileName() + "- err: " + qp.errorString();
+ return;
+ }
+
+ QObject *inst = qp.instance();
+ if(!inst) {
+ qWarning() << "Cannot create QObject instance from loaded library";
+ return;
+ }
+
+ original = qobject_cast(qp.instance());
+ if(!original) {
+ qWarning() << "Loaded library instance is not a Plugin*";
+ return;
+ }
+
+ clone = original->clone(this);
+ if(!clone) {
+ qWarning() << "clone method does not clone the object";
+ return;
+ }
+
+ QString cloneName;
+ cloneName = clone->name();
+
+ if(cloneName == "")
+ return;
+
+ m_plugin = clone;
+}
+
+ConnectionStrategy::ConnectionStrategy(QString uri, QObject *parent)
+ : m_uri(uri)
+ , QObject(parent)
+{}
+
+ConnectionStrategy::~ConnectionStrategy() {}
+
+bool ConnectionStrategy::validConnection(QString cat)
+{
+ bool valid = false;
+ if(cat.compare("iio") == 0) {
+ valid = iioConn();
+ } else {
+ valid = testConn();
+ }
+ return valid;
+}
+
+bool ConnectionStrategy::iioConn()
+{
+ iio_context *ctx = iio_create_context_from_uri(m_uri.toStdString().c_str());
+ if(!ctx) {
+ return false;
+ }
+ iio_context_destroy(ctx);
+ return true;
+}
+
+bool ConnectionStrategy::testConn() { return true; }