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

View file

@ -46,6 +46,7 @@ public:
[[nodiscard]] common::Level3ProductCategoryMap
GetAvailableLevel3Categories();
[[nodiscard]] const scwx::util::time_zone* GetDefaultTimeZone() const;
[[nodiscard]] std::optional<float> GetElevation() const;
[[nodiscard]] std::vector<float> GetElevationCuts() const;
[[nodiscard]] std::optional<float> GetIncomingLevel2Elevation() const;

View file

@ -18,6 +18,14 @@ namespace ui
static const std::string logPrefix_ = "scwx::qt::ui::animation_dock_widget";
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
{
public:
@ -47,9 +55,15 @@ public:
types::MapTime viewType_ {types::MapTime::Live};
bool isLive_ {true};
std::chrono::sys_days selectedDate_ {};
local_days selectedDate_ {};
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 UpdateAutoUpdateLabel();
};
@ -61,21 +75,19 @@ AnimationDockWidget::AnimationDockWidget(QWidget* parent) :
{
ui->setupUi(this);
// Set current date/time
QDateTime currentDateTime = QDateTime::currentDateTimeUtc();
QDate currentDate = currentDateTime.date();
QTime currentTime = currentDateTime.time();
currentTime = currentTime.addSecs(-currentTime.second() + 59);
ui->dateEdit->setDate(currentDate);
ui->timeEdit->setTime(currentTime);
ui->dateEdit->setMaximumDate(currentDateTime.date());
p->selectedDate_ = util::SysDays(currentDate);
p->selectedTime_ =
std::chrono::seconds(currentTime.msecsSinceStartOfDay() / 1000);
#if (__cpp_lib_chrono >= 201907L)
p->timeZone_ = std::chrono::get_tzdb().locate_zone("UTC");
#else
p->timeZone_ = date::get_tzdb().locate_zone("UTC");
#endif
const std::chrono::sys_seconds currentTimePoint =
std::chrono::floor<std::chrono::minutes>(
std::chrono::system_clock::now());
p->SetTimePoint(currentTimePoint);
// 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,
&QTimer::timeout,
this,
@ -108,6 +120,76 @@ AnimationDockWidget::~AnimationDockWidget()
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()
{
// View type
@ -142,12 +224,11 @@ void AnimationDockWidgetImpl::ConnectSignals()
{
if (date.isValid())
{
selectedDate_ = util::SysDays(date);
Q_EMIT self_->DateTimeChanged(selectedDate_ + selectedTime_);
selectedDate_ = util::LocalDays(date);
Q_EMIT self_->DateTimeChanged(GetTimePoint());
}
});
QObject::connect(
self_->ui->timeEdit,
QObject::connect(self_->ui->timeEdit,
&QDateTimeEdit::timeChanged,
self_,
[this](QTime time)
@ -155,8 +236,10 @@ void AnimationDockWidgetImpl::ConnectSignals()
if (time.isValid())
{
selectedTime_ =
std::chrono::seconds(time.msecsSinceStartOfDay() / 1000);
Q_EMIT self_->DateTimeChanged(selectedDate_ + selectedTime_);
std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::milliseconds(
time.msecsSinceStartOfDay()));
Q_EMIT self_->DateTimeChanged(GetTimePoint());
}
});
@ -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 qt
} // namespace scwx

View file

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

View file

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

View file

@ -17,6 +17,18 @@ std::chrono::sys_days SysDays(const QDate& date)
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 qt
} // namespace scwx

View file

@ -2,6 +2,10 @@
#include <chrono>
#if (__cpp_lib_chrono < 201907L)
# include <date/tz.h>
#endif
#include <QDateTime>
namespace scwx
@ -11,6 +15,12 @@ namespace qt
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.
*
@ -20,6 +30,15 @@ namespace util
*/
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 qt
} // namespace scwx