Make animation dock follow default timezones

This commit is contained in:
AdenKoperczak 2025-06-08 15:40:44 -04:00
parent 4306bb09ae
commit 26e24da4b5
No known key found for this signature in database
GPG key ID: 9843017036F62EE7
8 changed files with 205 additions and 47 deletions

View file

@ -141,6 +141,7 @@ public:
~MainWindowImpl() ~MainWindowImpl()
{ {
homeRadarConnection_.disconnect(); homeRadarConnection_.disconnect();
defaultTimeZoneConnection_.disconnect();
auto& generalSettings = settings::GeneralSettings::Instance(); auto& generalSettings = settings::GeneralSettings::Instance();
@ -245,6 +246,7 @@ public:
bool layerActionsInitialized_ {false}; bool layerActionsInitialized_ {false};
boost::signals2::scoped_connection homeRadarConnection_ {}; boost::signals2::scoped_connection homeRadarConnection_ {};
boost::signals2::scoped_connection defaultTimeZoneConnection_ {};
std::vector<map::MapWidget*> maps_; std::vector<map::MapWidget*> maps_;
@ -377,6 +379,7 @@ MainWindow::MainWindow(QWidget* parent) :
p->animationDockWidget_ = new ui::AnimationDockWidget(this); p->animationDockWidget_ = new ui::AnimationDockWidget(this);
p->timelineGroup_->GetContentsLayout()->addWidget(p->animationDockWidget_); p->timelineGroup_->GetContentsLayout()->addWidget(p->animationDockWidget_);
ui->radarToolboxScrollAreaContents->layout()->addWidget(p->timelineGroup_); ui->radarToolboxScrollAreaContents->layout()->addWidget(p->timelineGroup_);
p->animationDockWidget_->UpdateTimeZone(p->activeMap_->GetDefaultTimeZone());
// Reset toolbox spacer at the bottom // Reset toolbox spacer at the bottom
ui->radarToolboxScrollAreaContents->layout()->removeItem( ui->radarToolboxScrollAreaContents->layout()->removeItem(
@ -982,6 +985,16 @@ void MainWindowImpl::ConnectMapSignals()
void MainWindowImpl::ConnectAnimationSignals() void MainWindowImpl::ConnectAnimationSignals()
{ {
defaultTimeZoneConnection_ = settings::GeneralSettings::Instance()
.default_time_zone()
.changed_signal()
.connect(
[this]()
{
animationDockWidget_->UpdateTimeZone(
activeMap_->GetDefaultTimeZone());
});
connect(animationDockWidget_, connect(animationDockWidget_,
&ui::AnimationDockWidget::DateTimeChanged, &ui::AnimationDockWidget::DateTimeChanged,
timelineManager_.get(), timelineManager_.get(),
@ -1602,6 +1615,8 @@ void MainWindowImpl::UpdateRadarSite()
alertManager_->SetRadarSite(radarSite); alertManager_->SetRadarSite(radarSite);
placefileManager_->SetRadarSite(radarSite); placefileManager_->SetRadarSite(radarSite);
animationDockWidget_->UpdateTimeZone(activeMap_->GetDefaultTimeZone());
} }
void MainWindowImpl::UpdateVcp() void MainWindowImpl::UpdateVcp()

View file

@ -821,6 +821,11 @@ void MapWidget::SetSmoothingEnabled(bool smoothingEnabled)
} }
} }
const scwx::util::time_zone* MapWidget::GetDefaultTimeZone() const
{
return p->radarProductManager_->default_time_zone();
}
void MapWidget::SelectElevation(float elevation) void MapWidget::SelectElevation(float elevation)
{ {
auto radarProductView = p->context_->radar_product_view(); auto radarProductView = p->context_->radar_product_view();

View file

@ -45,9 +45,10 @@ public:
void DumpLayerList() const; void DumpLayerList() const;
[[nodiscard]] common::Level3ProductCategoryMap [[nodiscard]] common::Level3ProductCategoryMap
GetAvailableLevel3Categories(); GetAvailableLevel3Categories();
[[nodiscard]] std::optional<float> GetElevation() const; [[nodiscard]] const scwx::util::time_zone* GetDefaultTimeZone() const;
[[nodiscard]] std::vector<float> GetElevationCuts() const; [[nodiscard]] std::optional<float> GetElevation() const;
[[nodiscard]] std::vector<float> GetElevationCuts() const;
[[nodiscard]] std::optional<float> GetIncomingLevel2Elevation() const; [[nodiscard]] std::optional<float> GetIncomingLevel2Elevation() const;
[[nodiscard]] std::vector<std::string> GetLevel3Products(); [[nodiscard]] std::vector<std::string> GetLevel3Products();
[[nodiscard]] std::string GetMapStyle() const; [[nodiscard]] std::string GetMapStyle() const;

View file

@ -18,6 +18,14 @@ namespace ui
static const std::string logPrefix_ = "scwx::qt::ui::animation_dock_widget"; static const std::string logPrefix_ = "scwx::qt::ui::animation_dock_widget";
static const auto logger_ = scwx::util::Logger::Create(logPrefix_); static const auto logger_ = scwx::util::Logger::Create(logPrefix_);
#if (__cpp_lib_chrono >= 201907L)
using local_days = std::chrono::local_days;
using zoned_time_ = std::chrono::zoned_time<std::chrono::seconds>;
#else
using local_days = date::local_days;
using zoned_time_ = date::zoned_time<std::chrono::seconds>;
#endif
class AnimationDockWidgetImpl class AnimationDockWidgetImpl
{ {
public: public:
@ -47,8 +55,14 @@ public:
types::MapTime viewType_ {types::MapTime::Live}; types::MapTime viewType_ {types::MapTime::Live};
bool isLive_ {true}; bool isLive_ {true};
std::chrono::sys_days selectedDate_ {}; local_days selectedDate_ {};
std::chrono::seconds selectedTime_ {}; std::chrono::seconds selectedTime_ {};
const scwx::util::time_zone* timeZone_ {nullptr};
void UpdateTimeZoneLabel(const zoned_time_ zonedTime);
std::chrono::system_clock::time_point GetTimePoint();
void SetTimePoint(std::chrono::system_clock::time_point time);
void ConnectSignals(); void ConnectSignals();
void UpdateAutoUpdateLabel(); void UpdateAutoUpdateLabel();
@ -61,21 +75,19 @@ AnimationDockWidget::AnimationDockWidget(QWidget* parent) :
{ {
ui->setupUi(this); ui->setupUi(this);
// Set current date/time #if (__cpp_lib_chrono >= 201907L)
QDateTime currentDateTime = QDateTime::currentDateTimeUtc(); p->timeZone_ = std::chrono::get_tzdb().locate_zone("UTC");
QDate currentDate = currentDateTime.date(); #else
QTime currentTime = currentDateTime.time(); p->timeZone_ = date::get_tzdb().locate_zone("UTC");
currentTime = currentTime.addSecs(-currentTime.second() + 59); #endif
const std::chrono::sys_seconds currentTimePoint =
ui->dateEdit->setDate(currentDate); std::chrono::floor<std::chrono::minutes>(
ui->timeEdit->setTime(currentTime); std::chrono::system_clock::now());
ui->dateEdit->setMaximumDate(currentDateTime.date()); p->SetTimePoint(currentTimePoint);
p->selectedDate_ = util::SysDays(currentDate);
p->selectedTime_ =
std::chrono::seconds(currentTime.msecsSinceStartOfDay() / 1000);
// Update maximum date on a timer // Update maximum date on a timer
QTimer* maxDateTimer = new QTimer(this); // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) Qt Owns this memory
auto* maxDateTimer = new QTimer(this);
connect(maxDateTimer, connect(maxDateTimer,
&QTimer::timeout, &QTimer::timeout,
this, this,
@ -108,6 +120,76 @@ AnimationDockWidget::~AnimationDockWidget()
delete ui; delete ui;
} }
void AnimationDockWidgetImpl::UpdateTimeZoneLabel(const zoned_time_ zonedTime)
{
#if (__cpp_lib_chrono >= 201907L)
namespace df = std;
static constexpr std::string_view kFormatStringTimezone = "{:%Z}";
#else
namespace df = date;
static constexpr std::string kFormatStringTimezone = "%Z";
#endif
const std::string timeZoneStr = df::format(kFormatStringTimezone, zonedTime);
self_->ui->timeZoneLabel->setText(timeZoneStr.c_str());
}
std::chrono::system_clock::time_point AnimationDockWidgetImpl::GetTimePoint()
{
#if (__cpp_lib_chrono >= 201907L)
using namespace std::chrono;
#else
using namespace date;
#endif
// Convert the local time, to a zoned time, to a system time
const local_time<std::chrono::seconds> localTime =
selectedDate_ + selectedTime_;
const auto zonedTime =
zoned_time<std::chrono::seconds>(timeZone_, localTime);
const std::chrono::sys_time systemTime = zonedTime.get_sys_time();
// This is done to update it when the date changes
UpdateTimeZoneLabel(zonedTime);
return systemTime;
}
void AnimationDockWidgetImpl::SetTimePoint(
std::chrono::system_clock::time_point systemTime)
{
#if (__cpp_lib_chrono >= 201907L)
using namespace std::chrono;
#else
using namespace date;
#endif
// Convert the time to a local time
auto systemTimeSeconds = time_point_cast<std::chrono::seconds>(systemTime);
auto zonedTime =
zoned_time<std::chrono::seconds>(timeZone_, systemTimeSeconds);
const local_seconds localTime = zonedTime.get_local_time();
// Get the date and time as seperate fields
selectedDate_ = floor<days>(localTime);
selectedTime_ = localTime - selectedDate_;
// Pull out the local date and time as qt times (with c++20 this could be
// simplified)
auto time = QTime::fromMSecsSinceStartOfDay(static_cast<int>(
duration_cast<std::chrono::milliseconds>(selectedTime_).count()));
auto yearMonthDay = year_month_day(selectedDate_);
auto date = QDate(int(yearMonthDay.year()),
// These are always in a small range, so cast is safe
static_cast<int>(unsigned(yearMonthDay.month())),
static_cast<int>(unsigned(yearMonthDay.day())));
// Update labels
self_->ui->timeEdit->setTime(time);
self_->ui->dateEdit->setDate(date);
// Time zone almost certainly just changed, so update it
UpdateTimeZoneLabel(zonedTime);
}
void AnimationDockWidgetImpl::ConnectSignals() void AnimationDockWidgetImpl::ConnectSignals()
{ {
// View type // View type
@ -142,23 +224,24 @@ void AnimationDockWidgetImpl::ConnectSignals()
{ {
if (date.isValid()) if (date.isValid())
{ {
selectedDate_ = util::SysDays(date); selectedDate_ = util::LocalDays(date);
Q_EMIT self_->DateTimeChanged(selectedDate_ + selectedTime_); Q_EMIT self_->DateTimeChanged(GetTimePoint());
}
});
QObject::connect(
self_->ui->timeEdit,
&QDateTimeEdit::timeChanged,
self_,
[this](QTime time)
{
if (time.isValid())
{
selectedTime_ =
std::chrono::seconds(time.msecsSinceStartOfDay() / 1000);
Q_EMIT self_->DateTimeChanged(selectedDate_ + selectedTime_);
} }
}); });
QObject::connect(self_->ui->timeEdit,
&QDateTimeEdit::timeChanged,
self_,
[this](QTime time)
{
if (time.isValid())
{
selectedTime_ =
std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::milliseconds(
time.msecsSinceStartOfDay()));
Q_EMIT self_->DateTimeChanged(GetTimePoint());
}
});
// Loop controls // Loop controls
QObject::connect( QObject::connect(
@ -302,6 +385,27 @@ void AnimationDockWidgetImpl::UpdateAutoUpdateLabel()
} }
} }
void AnimationDockWidget::UpdateTimeZone(const scwx::util::time_zone* timeZone)
{
// null timezone is really UTC. This simplifies other code.
if (timeZone == nullptr)
{
#if (__cpp_lib_chrono >= 201907L)
timeZone = std::chrono::get_tzdb().locate_zone("UTC");
#else
timeZone = date::get_tzdb().locate_zone("UTC");
#endif
}
// Get the (UTC relative) time that is selected. We want to preserve this
// across timezone changes.
auto currentTime = p->GetTimePoint();
p->timeZone_ = timeZone;
// Set the (UTC relative) time that was already selected. This ensures that
// the actual time does not change, only the time zone.
p->SetTimePoint(currentTime);
}
} // namespace ui } // namespace ui
} // namespace qt } // namespace qt
} // namespace scwx } // namespace scwx

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <scwx/qt/types/map_types.hpp> #include <scwx/qt/types/map_types.hpp>
#include <scwx/util/time.hpp>
#include <chrono> #include <chrono>
@ -32,6 +33,7 @@ public slots:
void UpdateAnimationState(types::AnimationState state); void UpdateAnimationState(types::AnimationState state);
void UpdateLiveState(bool isLive); void UpdateLiveState(bool isLive);
void UpdateViewType(types::MapTime viewType); void UpdateViewType(types::MapTime viewType);
void UpdateTimeZone(const scwx::util::time_zone* timeZone);
signals: signals:
void ViewTypeChanged(types::MapTime viewType); void ViewTypeChanged(types::MapTime viewType);

View file

@ -7,14 +7,14 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>189</width> <width>189</width>
<height>264</height> <height>276</height>
</rect> </rect>
</property> </property>
<property name="frameShape"> <property name="frameShape">
<enum>QFrame::StyledPanel</enum> <enum>QFrame::Shape::StyledPanel</enum>
</property> </property>
<property name="frameShadow"> <property name="frameShadow">
<enum>QFrame::Raised</enum> <enum>QFrame::Shadow::Raised</enum>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_3">
<property name="leftMargin"> <property name="leftMargin">
@ -56,7 +56,7 @@
<item> <item>
<widget class="QDateEdit" name="dateEdit"> <widget class="QDateEdit" name="dateEdit">
<property name="correctionMode"> <property name="correctionMode">
<enum>QAbstractSpinBox::CorrectToNearestValue</enum> <enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property> </property>
<property name="minimumDateTime"> <property name="minimumDateTime">
<datetime> <datetime>
@ -79,10 +79,10 @@
<item> <item>
<widget class="QFrame" name="frame"> <widget class="QFrame" name="frame">
<property name="frameShape"> <property name="frameShape">
<enum>QFrame::StyledPanel</enum> <enum>QFrame::Shape::StyledPanel</enum>
</property> </property>
<property name="frameShadow"> <property name="frameShadow">
<enum>QFrame::Raised</enum> <enum>QFrame::Shadow::Raised</enum>
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin"> <property name="leftMargin">
@ -100,7 +100,7 @@
<item> <item>
<widget class="QTimeEdit" name="timeEdit"> <widget class="QTimeEdit" name="timeEdit">
<property name="correctionMode"> <property name="correctionMode">
<enum>QAbstractSpinBox::CorrectToNearestValue</enum> <enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property> </property>
<property name="displayFormat"> <property name="displayFormat">
<string>HH:mm</string> <string>HH:mm</string>
@ -108,7 +108,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLabel" name="label"> <widget class="QLabel" name="timeZoneLabel">
<property name="text"> <property name="text">
<string>UTC</string> <string>UTC</string>
</property> </property>
@ -120,10 +120,10 @@
<item> <item>
<widget class="QFrame" name="frame_3"> <widget class="QFrame" name="frame_3">
<property name="frameShape"> <property name="frameShape">
<enum>QFrame::StyledPanel</enum> <enum>QFrame::Shape::StyledPanel</enum>
</property> </property>
<property name="frameShadow"> <property name="frameShadow">
<enum>QFrame::Raised</enum> <enum>QFrame::Shadow::Raised</enum>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<property name="leftMargin"> <property name="leftMargin">
@ -141,7 +141,7 @@
<item row="0" column="1"> <item row="0" column="1">
<widget class="QSpinBox" name="loopTimeSpinBox"> <widget class="QSpinBox" name="loopTimeSpinBox">
<property name="correctionMode"> <property name="correctionMode">
<enum>QAbstractSpinBox::CorrectToNearestValue</enum> <enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property> </property>
<property name="suffix"> <property name="suffix">
<string> min</string> <string> min</string>
@ -193,7 +193,7 @@
<item row="1" column="1"> <item row="1" column="1">
<widget class="QDoubleSpinBox" name="loopSpeedSpinBox"> <widget class="QDoubleSpinBox" name="loopSpeedSpinBox">
<property name="correctionMode"> <property name="correctionMode">
<enum>QAbstractSpinBox::CorrectToNearestValue</enum> <enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property> </property>
<property name="suffix"> <property name="suffix">
<string>x</string> <string>x</string>
@ -219,10 +219,10 @@
<item> <item>
<widget class="QFrame" name="frame_2"> <widget class="QFrame" name="frame_2">
<property name="frameShape"> <property name="frameShape">
<enum>QFrame::StyledPanel</enum> <enum>QFrame::Shape::StyledPanel</enum>
</property> </property>
<property name="frameShadow"> <property name="frameShadow">
<enum>QFrame::Raised</enum> <enum>QFrame::Shadow::Raised</enum>
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout_2"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing"> <property name="spacing">

View file

@ -17,6 +17,18 @@ std::chrono::sys_days SysDays(const QDate& date)
julianEpoch); julianEpoch);
} }
local_days LocalDays(const QDate& date)
{
#if (__cpp_lib_chrono >= 201907L)
using namespace std::chrono;
#else
using namespace date;
#endif
auto yearMonthDay =
year_month_day(year(date.year()), month(date.month()), day(date.day()));
return local_days(yearMonthDay);
}
} // namespace util } // namespace util
} // namespace qt } // namespace qt
} // namespace scwx } // namespace scwx

View file

@ -2,6 +2,10 @@
#include <chrono> #include <chrono>
#if (__cpp_lib_chrono < 201907L)
# include <date/tz.h>
#endif
#include <QDateTime> #include <QDateTime>
namespace scwx namespace scwx
@ -11,6 +15,12 @@ namespace qt
namespace util namespace util
{ {
#if (__cpp_lib_chrono >= 201907L)
using local_days = std::chrono::local_days;
#else
using local_days = date::local_days;
#endif
/** /**
* @brief Convert QDate to std::chrono::sys_days. * @brief Convert QDate to std::chrono::sys_days.
* *
@ -20,6 +30,15 @@ namespace util
*/ */
std::chrono::sys_days SysDays(const QDate& date); std::chrono::sys_days SysDays(const QDate& date);
/**
* @brief Convert QDate to std::chrono::local_days.
*
* @param [in] date Date to convert
*
* @return Days
*/
local_days LocalDays(const QDate& date);
} // namespace util } // namespace util
} // namespace qt } // namespace qt
} // namespace scwx } // namespace scwx