Merge branch 'feature/resource-explorer' into develop

This commit is contained in:
Dan Paulat 2022-09-22 00:29:46 -05:00
commit b2b413c48d
12 changed files with 708 additions and 6 deletions

View file

@ -50,9 +50,11 @@ set(HDR_GL_DRAW source/scwx/qt/gl/draw/draw_item.hpp
set(SRC_GL_DRAW source/scwx/qt/gl/draw/draw_item.cpp
source/scwx/qt/gl/draw/rectangle.cpp)
set(HDR_MANAGER source/scwx/qt/manager/radar_product_manager.hpp
source/scwx/qt/manager/radar_product_manager_notifier.hpp
source/scwx/qt/manager/resource_manager.hpp
source/scwx/qt/manager/settings_manager.hpp)
set(SRC_MANAGER source/scwx/qt/manager/radar_product_manager.cpp
source/scwx/qt/manager/radar_product_manager_notifier.cpp
source/scwx/qt/manager/resource_manager.cpp
source/scwx/qt/manager/settings_manager.cpp)
set(HDR_MAP source/scwx/qt/map/color_table_layer.hpp
@ -73,6 +75,12 @@ set(SRC_MAP source/scwx/qt/map/color_table_layer.cpp
source/scwx/qt/map/overlay_layer.cpp
source/scwx/qt/map/radar_product_layer.cpp
source/scwx/qt/map/radar_range_layer.cpp)
set(HDR_MODEL source/scwx/qt/model/radar_product_model.hpp
source/scwx/qt/model/tree_item.hpp
source/scwx/qt/model/tree_model.hpp)
set(SRC_MODEL source/scwx/qt/model/radar_product_model.cpp
source/scwx/qt/model/tree_item.cpp
source/scwx/qt/model/tree_model.cpp)
set(HDR_REQUEST source/scwx/qt/request/nexrad_file_request.hpp)
set(SRC_REQUEST source/scwx/qt/request/nexrad_file_request.cpp)
set(HDR_SETTINGS source/scwx/qt/settings/general_settings.hpp
@ -140,6 +148,8 @@ set(PROJECT_SOURCES ${HDR_MAIN}
${UI_MAIN}
${HDR_MAP}
${SRC_MAP}
${HDR_MODEL}
${SRC_MODEL}
${HDR_REQUEST}
${SRC_REQUEST}
${HDR_SETTINGS}
@ -172,6 +182,8 @@ source_group("Source Files\\manager" FILES ${SRC_MANAGER})
source_group("UI Files\\main" FILES ${UI_MAIN})
source_group("Header Files\\map" FILES ${HDR_MAP})
source_group("Source Files\\map" FILES ${SRC_MAP})
source_group("Header Files\\model" FILES ${HDR_MODEL})
source_group("Source Files\\model" FILES ${SRC_MODEL})
source_group("Header Files\\request" FILES ${HDR_REQUEST})
source_group("Source Files\\request" FILES ${SRC_REQUEST})
source_group("Header Files\\settings" FILES ${HDR_SETTINGS})

View file

@ -6,6 +6,7 @@
#include <scwx/qt/manager/radar_product_manager.hpp>
#include <scwx/qt/manager/settings_manager.hpp>
#include <scwx/qt/map/map_widget.hpp>
#include <scwx/qt/model/radar_product_model.hpp>
#include <scwx/qt/ui/flow_layout.hpp>
#include <scwx/qt/ui/level2_products_widget.hpp>
#include <scwx/qt/ui/level2_settings_widget.hpp>
@ -123,6 +124,24 @@ MainWindow::MainWindow(QWidget* parent) :
ui->vcpValueLabel->setVisible(false);
ui->vcpDescriptionLabel->setVisible(false);
// Configure Menu
ui->menuView->insertAction(ui->actionRadarToolbox,
ui->radarToolboxDock->toggleViewAction());
ui->radarToolboxDock->toggleViewAction()->setText(tr("Radar &Toolbox"));
ui->actionRadarToolbox->setVisible(false);
ui->menuView->insertAction(ui->actionResourceExplorer,
ui->resourceExplorerDock->toggleViewAction());
ui->resourceExplorerDock->toggleViewAction()->setText(
tr("&Resource Explorer"));
ui->actionResourceExplorer->setVisible(false);
// Configure Docks
ui->resourceExplorerDock->setVisible(false);
ui->resourceTreeView->setModel(new model::RadarProductModel(this));
// Configure Map
p->ConfigureMapLayout();
// Add Level 2 Products

View file

@ -52,13 +52,21 @@
</property>
<addaction name="actionAboutSupercellWx"/>
</widget>
<widget class="QMenu" name="menuView">
<property name="title">
<string>&amp;View</string>
</property>
<addaction name="actionRadarToolbox"/>
<addaction name="actionResourceExplorer"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuView"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QDockWidget" name="radarToolboxDock">
<property name="windowTitle">
<string>Toolbox</string>
<string>Radar Toolbox</string>
</property>
<attribute name="dockWidgetArea">
<number>1</number>
@ -217,6 +225,21 @@
</layout>
</widget>
</widget>
<widget class="QDockWidget" name="resourceExplorerDock">
<property name="windowTitle">
<string>Resource Explorer</string>
</property>
<attribute name="dockWidgetArea">
<number>2</number>
</attribute>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QTreeView" name="resourceTreeView"/>
</item>
</layout>
</widget>
</widget>
<action name="actionExit">
<property name="text">
<string>E&amp;xit</string>
@ -235,6 +258,16 @@
<string>Ctrl+O</string>
</property>
</action>
<action name="actionRadarToolbox">
<property name="text">
<string>Radar Toolbox</string>
</property>
</action>
<action name="actionResourceExplorer">
<property name="text">
<string>&amp;Resource Explorer</string>
</property>
</action>
</widget>
<resources/>
<connections/>

View file

@ -1,4 +1,5 @@
#include <scwx/qt/manager/radar_product_manager.hpp>
#include <scwx/qt/manager/radar_product_manager_notifier.hpp>
#include <scwx/common/constants.hpp>
#include <scwx/provider/nexrad_data_provider_factory.hpp>
#include <scwx/util/logger.hpp>
@ -937,15 +938,29 @@ void RadarProductManager::UpdateAvailableProducts()
std::shared_ptr<RadarProductManager>
RadarProductManager::Instance(const std::string& radarSite)
{
std::lock_guard<std::mutex> guard(instanceMutex_);
std::shared_ptr<RadarProductManager> instance = nullptr;
bool instanceCreated = false;
if (!instanceMap_.contains(radarSite))
{
instanceMap_[radarSite] =
std::make_shared<RadarProductManager>(radarSite);
std::lock_guard<std::mutex> guard(instanceMutex_);
if (!instanceMap_.contains(radarSite))
{
instanceMap_[radarSite] =
std::make_shared<RadarProductManager>(radarSite);
instanceCreated = true;
}
instance = instanceMap_[radarSite];
}
return instanceMap_[radarSite];
if (instanceCreated)
{
emit RadarProductManagerNotifier::Instance().RadarProductManagerCreated(
radarSite);
}
return instance;
}
} // namespace manager

View file

@ -0,0 +1,34 @@
#include <scwx/qt/manager/radar_product_manager_notifier.hpp>
namespace scwx
{
namespace qt
{
namespace manager
{
static const std::string logPrefix_ =
"scwx::qt::manager::radar_product_manager_notifier";
class RadarProductManagerNotifierImpl
{
public:
explicit RadarProductManagerNotifierImpl() {}
~RadarProductManagerNotifierImpl() {}
};
RadarProductManagerNotifier::RadarProductManagerNotifier() :
p(std::make_unique<RadarProductManagerNotifierImpl>())
{
}
RadarProductManagerNotifier::~RadarProductManagerNotifier() = default;
RadarProductManagerNotifier& RadarProductManagerNotifier::Instance()
{
static RadarProductManagerNotifier instance_ {};
return instance_;
}
} // namespace manager
} // namespace qt
} // namespace scwx

View file

@ -0,0 +1,35 @@
#pragma once
#include <memory>
#include <QObject>
namespace scwx
{
namespace qt
{
namespace manager
{
class RadarProductManagerNotifierImpl;
class RadarProductManagerNotifier : public QObject
{
Q_OBJECT
public:
explicit RadarProductManagerNotifier();
~RadarProductManagerNotifier();
static RadarProductManagerNotifier& Instance();
signals:
void RadarProductManagerCreated(const std::string& radarSite);
private:
std::unique_ptr<RadarProductManagerNotifierImpl> p;
};
} // namespace manager
} // namespace qt
} // namespace scwx

View file

@ -0,0 +1,146 @@
#include <scwx/qt/model/radar_product_model.hpp>
#include <scwx/qt/manager/radar_product_manager.hpp>
#include <scwx/qt/manager/radar_product_manager_notifier.hpp>
#include <scwx/util/logger.hpp>
#include <scwx/util/time.hpp>
namespace scwx
{
namespace qt
{
namespace model
{
static const std::string logPrefix_ = "scwx::qt::model::radar_product_model";
static const auto logger_ = scwx::util::Logger::Create(logPrefix_);
class RadarProductModelImpl : public QObject
{
Q_OBJECT
public:
explicit RadarProductModelImpl(RadarProductModel* self);
~RadarProductModelImpl() = default;
RadarProductModel* self_;
std::shared_ptr<TreeItem> rootItem_;
};
RadarProductModel::RadarProductModel(QObject* parent) :
TreeModel(parent), p(std::make_unique<RadarProductModelImpl>(this))
{
}
RadarProductModel::~RadarProductModel() = default;
const std::shared_ptr<TreeItem> RadarProductModel::root_item() const
{
return p->rootItem_;
}
RadarProductModelImpl::RadarProductModelImpl(RadarProductModel* self) :
self_ {self},
rootItem_ {std::make_shared<TreeItem>(
std::vector<QVariant> {QObject::tr("Product")})}
{
connect(
&manager::RadarProductManagerNotifier::Instance(),
&manager::RadarProductManagerNotifier::RadarProductManagerCreated,
this,
[=](const std::string& radarSite)
{
logger_->debug("Adding radar site: {}", radarSite);
const QModelIndex rootIndex =
self_->createIndex(rootItem_->row(), 0, rootItem_.get());
const int rootChildren = rootItem_->child_count();
self_->beginInsertRows(rootIndex, rootChildren, rootChildren);
TreeItem* radarSiteItem =
new TreeItem({QString::fromStdString(radarSite)});
rootItem_->AppendChild(radarSiteItem);
self_->endInsertRows();
connect(
manager::RadarProductManager::Instance(radarSite).get(),
&manager::RadarProductManager::NewDataAvailable,
this,
[=](common::RadarProductGroup group,
const std::string& product,
std::chrono::system_clock::time_point latestTime)
{
const QString groupName {QString::fromStdString(
common::GetRadarProductGroupName(group))};
// Find existing group item (e.g., Level 2, Level 3)
TreeItem* groupItem = radarSiteItem->FindChild(0, groupName);
if (groupItem == nullptr)
{
// Existing group item was not found, create it
const QModelIndex radarSiteIndex =
self_->createIndex(radarSiteItem->row(), 0, radarSiteItem);
const int radarSiteChildren = radarSiteItem->child_count();
self_->beginInsertRows(
radarSiteIndex, radarSiteChildren, radarSiteChildren);
groupItem = new TreeItem({groupName});
radarSiteItem->AppendChild(groupItem);
self_->endInsertRows();
}
TreeItem* productItem = nullptr;
if (group == common::RadarProductGroup::Level2)
{
// Level 2 items are not separated by product
productItem = groupItem;
}
else
{
// Find existing product item (e.g., N0B, N0Q)
const QString productName {QString::fromStdString(product)};
productItem = groupItem->FindChild(0, productName);
if (productItem == nullptr)
{
// Existing product item was not found, create it
const QModelIndex groupItemIndex =
self_->createIndex(groupItem->row(), 0, groupItem);
const int groupItemChildren = groupItem->child_count();
self_->beginInsertRows(
groupItemIndex, groupItemChildren, groupItemChildren);
productItem = new TreeItem({productName});
groupItem->AppendChild(productItem);
self_->endInsertRows();
}
}
// Create leaf item for product time
const QModelIndex productItemIndex =
self_->createIndex(productItem->row(), 0, productItem);
const int productItemChildren = productItem->child_count();
self_->beginInsertRows(
productItemIndex, productItemChildren, productItemChildren);
productItem->AppendChild(new TreeItem {
QString::fromStdString(util::TimeString(latestTime))});
self_->endInsertRows();
},
Qt::QueuedConnection);
});
}
#include "radar_product_model.moc"
} // namespace model
} // namespace qt
} // namespace scwx

View file

@ -0,0 +1,31 @@
#pragma once
#include <scwx/qt/model/tree_model.hpp>
namespace scwx
{
namespace qt
{
namespace model
{
class RadarProductModelImpl;
class RadarProductModel : public TreeModel
{
public:
explicit RadarProductModel(QObject* parent = nullptr);
~RadarProductModel();
protected:
const std::shared_ptr<TreeItem> root_item() const override;
private:
std::unique_ptr<RadarProductModelImpl> p;
friend class RadarProductModelImpl;
};
} // namespace model
} // namespace qt
} // namespace scwx

View file

@ -0,0 +1,138 @@
#include <scwx/qt/model/tree_item.hpp>
namespace scwx
{
namespace qt
{
namespace model
{
static const std::string logPrefix_ = "scwx::qt::model::tree_item";
class TreeItem::Impl
{
public:
explicit Impl(const std::vector<QVariant>& data, TreeItem* parent) :
childItems_ {}, itemData_ {data}, parentItem_ {parent}
{
}
~Impl() { qDeleteAll(childItems_); }
std::vector<TreeItem*> childItems_;
std::vector<QVariant> itemData_;
TreeItem* parentItem_;
};
TreeItem::TreeItem(const std::vector<QVariant>& data, TreeItem* parent) :
p {std::make_unique<Impl>(data, parent)}
{
}
TreeItem::TreeItem(std::initializer_list<QVariant> data, TreeItem* parent) :
TreeItem(std::vector<QVariant> {data}, parent)
{
}
TreeItem::~TreeItem() {}
TreeItem::TreeItem(TreeItem&&) noexcept = default;
TreeItem& TreeItem::operator=(TreeItem&&) noexcept = default;
void TreeItem::AppendChild(TreeItem* item)
{
item->p->parentItem_ = this;
p->childItems_.push_back(item);
}
TreeItem* TreeItem::FindChild(int column, const QVariant& data)
{
auto it = std::find_if(p->childItems_.begin(),
p->childItems_.end(),
[&](auto& item) {
return (column < item->column_count() &&
item->data(column) == data);
});
TreeItem* item = nullptr;
if (it != p->childItems_.end())
{
item = *it;
}
return item;
}
const TreeItem* TreeItem::child(int row) const
{
const TreeItem* item = nullptr;
if (0 <= row && row < p->childItems_.size())
{
item = p->childItems_[row];
}
return item;
}
TreeItem* TreeItem::child(int row)
{
TreeItem* item = nullptr;
if (0 <= row && row < p->childItems_.size())
{
item = p->childItems_[row];
}
return item;
}
std::vector<TreeItem*> TreeItem::children()
{
return p->childItems_;
}
int TreeItem::child_count() const
{
return static_cast<int>(p->childItems_.size());
}
int TreeItem::column_count() const
{
return static_cast<int>(p->itemData_.size());
}
QVariant TreeItem::data(int column) const
{
if (0 <= column && column < p->itemData_.size())
{
return p->itemData_[column];
}
else
{
return QVariant();
}
}
int TreeItem::row() const
{
int row = 0;
if (p->parentItem_ != nullptr)
{
const auto& childItems = p->parentItem_->p->childItems_;
row =
std::distance(childItems.cbegin(),
std::find(childItems.cbegin(), childItems.cend(), this));
}
return row;
}
const TreeItem* TreeItem::parent_item() const
{
return p->parentItem_;
}
} // namespace model
} // namespace qt
} // namespace scwx

View file

@ -0,0 +1,49 @@
#pragma once
#include <memory>
#include <vector>
#include <QVariant>
namespace scwx
{
namespace qt
{
namespace model
{
class TreeItem
{
public:
explicit TreeItem(const std::vector<QVariant>& data,
TreeItem* parent = nullptr);
explicit TreeItem(std::initializer_list<QVariant> data,
TreeItem* parent = nullptr);
virtual ~TreeItem();
TreeItem(const TreeItem&) = delete;
TreeItem& operator=(const TreeItem&) = delete;
TreeItem(TreeItem&&) noexcept;
TreeItem& operator=(TreeItem&&) noexcept;
void AppendChild(TreeItem* child);
TreeItem* FindChild(int column, const QVariant& data);
const TreeItem* child(int row) const;
TreeItem* child(int row);
std::vector<TreeItem*> children();
int child_count() const;
int column_count() const;
QVariant data(int column) const;
int row() const;
const TreeItem* parent_item() const;
private:
class Impl;
std::unique_ptr<Impl> p;
};
} // namespace model
} // namespace qt
} // namespace scwx

View file

@ -0,0 +1,145 @@
#include <scwx/qt/model/tree_model.hpp>
namespace scwx
{
namespace qt
{
namespace model
{
static const std::string logPrefix_ = "scwx::qt::model::tree_model";
class TreeModelImpl
{
public:
explicit TreeModelImpl() = default;
~TreeModelImpl() = default;
};
TreeModel::TreeModel(QObject* parent) :
QAbstractItemModel(parent), p(std::make_unique<TreeModelImpl>())
{
}
TreeModel::~TreeModel() = default;
int TreeModel::rowCount(const QModelIndex& parent) const
{
const TreeItem* parentItem;
if (parent.isValid())
{
parentItem = static_cast<const TreeItem*>(parent.constInternalPointer());
}
else
{
parentItem = root_item().get();
}
return parentItem->child_count();
}
int TreeModel::columnCount(const QModelIndex& parent) const
{
const TreeItem* parentItem;
if (parent.isValid())
{
parentItem = static_cast<const TreeItem*>(parent.constInternalPointer());
}
else
{
parentItem = root_item().get();
}
return parentItem->column_count();
}
QVariant TreeModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid() || role != Qt::DisplayRole)
{
return QVariant();
}
const TreeItem* item = static_cast<const TreeItem*>(index.internalPointer());
return item->data(index.column());
}
Qt::ItemFlags TreeModel::flags(const QModelIndex& index) const
{
Qt::ItemFlags flags;
if (!index.isValid())
{
flags = Qt::NoItemFlags;
}
else
{
flags = QAbstractItemModel::flags(index);
}
return flags;
}
QVariant
TreeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
{
return root_item()->data(section);
}
return QVariant();
}
QModelIndex
TreeModel::index(int row, int column, const QModelIndex& parent) const
{
if (!hasIndex(row, column, parent))
{
return QModelIndex();
}
const TreeItem* parentItem;
if (!parent.isValid())
{
parentItem = root_item().get();
}
else
{
parentItem = static_cast<const TreeItem*>(parent.constInternalPointer());
}
const TreeItem* childItem = parentItem->child(row);
if (childItem)
{
return createIndex(row, column, childItem);
}
return QModelIndex();
}
QModelIndex TreeModel::parent(const QModelIndex& index) const
{
if (!index.isValid())
{
return QModelIndex();
}
const TreeItem* childItem =
static_cast<const TreeItem*>(index.constInternalPointer());
const TreeItem* parentItem = childItem->parent_item();
if (parentItem == root_item().get() || parentItem == nullptr)
{
return QModelIndex();
}
return createIndex(parentItem->row(), 0, parentItem);
}
} // namespace model
} // namespace qt
} // namespace scwx

View file

@ -0,0 +1,45 @@
#pragma once
#include <scwx/qt/model/tree_item.hpp>
#include <QAbstractItemModel>
namespace scwx
{
namespace qt
{
namespace model
{
class TreeModelImpl;
class TreeModel : public QAbstractItemModel
{
public:
explicit TreeModel(QObject* parent = nullptr);
virtual ~TreeModel();
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index,
int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
QVariant headerData(int section,
Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
QModelIndex index(int row,
int column,
const QModelIndex& parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex& index) const override;
protected:
virtual const std::shared_ptr<TreeItem> root_item() const = 0;
private:
std::unique_ptr<TreeModelImpl> p;
};
} // namespace model
} // namespace qt
} // namespace scwx