Add native macOS settings and device integration
This commit is contained in:
+131
-1
@@ -9,6 +9,7 @@
|
||||
#include <QAbstractItemView>
|
||||
#include <QAbstractTableModel>
|
||||
#include <QCheckBox>
|
||||
#include <QCloseEvent>
|
||||
#include <QComboBox>
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
@@ -21,10 +22,13 @@
|
||||
#include <QHeaderView>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QMenuBar>
|
||||
#include <QMessageBox>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QScrollArea>
|
||||
#include <QPushButton>
|
||||
#include <QRegularExpression>
|
||||
#include <QSettings>
|
||||
#include <QSpinBox>
|
||||
#include <QSplitter>
|
||||
#include <QStatusBar>
|
||||
@@ -365,6 +369,7 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
, m_rinex(new RinexNavigation(this))
|
||||
{
|
||||
buildUi();
|
||||
buildMenus();
|
||||
|
||||
m_locationTimeoutTimer = new QTimer(this);
|
||||
m_locationTimeoutTimer->setSingleShot(true);
|
||||
@@ -374,11 +379,18 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
m_uiRefreshTimer->setSingleShot(true);
|
||||
m_uiRefreshTimer->setInterval(UiRefreshIntervalMs);
|
||||
|
||||
m_portRefreshTimer = new QTimer(this);
|
||||
m_portRefreshTimer->setInterval(2000);
|
||||
|
||||
connect(m_serial, &SerialReader::lineReceived, this, &MainWindow::handleSerialLine);
|
||||
connect(m_serial, &SerialReader::statusChanged, this, &MainWindow::handleSerialStatus);
|
||||
connect(m_serial, &SerialReader::errorOccurred, this, &MainWindow::handleSerialError);
|
||||
connect(m_locationTimeoutTimer, &QTimer::timeout, this, &MainWindow::handleLocationTimeout);
|
||||
connect(m_uiRefreshTimer, &QTimer::timeout, this, &MainWindow::flushUiUpdates);
|
||||
connect(m_portRefreshTimer, &QTimer::timeout, this, [this] {
|
||||
if (!m_serial->isOpen()) refreshSerialPorts();
|
||||
});
|
||||
m_portRefreshTimer->start();
|
||||
connect(m_rinex, &RinexNavigation::satelliteMotionUpdated,
|
||||
m_globe, &GlobeWidget::setSatelliteMotion);
|
||||
connect(m_rinex, &RinexNavigation::statusChanged, this,
|
||||
@@ -399,9 +411,16 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
rinexRefreshTimer->start();
|
||||
|
||||
resize(1480, 900);
|
||||
loadSettings();
|
||||
setWindowTitle(QStringLiteral("Canli Konum Haritasi"));
|
||||
}
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
saveSettings();
|
||||
QMainWindow::closeEvent(event);
|
||||
}
|
||||
|
||||
void MainWindow::toggleSerial()
|
||||
{
|
||||
if (m_serial->isOpen()) {
|
||||
@@ -475,6 +494,14 @@ void MainWindow::refreshSerialPorts()
|
||||
const QString configuredPort = qEnvironmentVariable("LOCATION_PLOTTER_PORT").trimmed();
|
||||
const QStringList detectedPorts = detectedSerialPorts();
|
||||
|
||||
if (m_portsInitialized && detectedPorts == m_lastDetectedPorts) {
|
||||
return;
|
||||
}
|
||||
const bool deviceAdded = m_portsInitialized
|
||||
&& detectedPorts.size() > m_lastDetectedPorts.size();
|
||||
m_lastDetectedPorts = detectedPorts;
|
||||
m_portsInitialized = true;
|
||||
|
||||
m_portCombo->clear();
|
||||
for (const QString &port : detectedPorts) {
|
||||
m_portCombo->addItem(port);
|
||||
@@ -495,6 +522,106 @@ void MainWindow::refreshSerialPorts()
|
||||
m_portCombo->addItem(selectedPort);
|
||||
}
|
||||
m_portCombo->setCurrentText(selectedPort);
|
||||
if (deviceAdded) {
|
||||
statusBar()->showMessage(QStringLiteral("Yeni USB UART aygiti algilandi: %1")
|
||||
.arg(selectedPort), 4000);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::buildMenus()
|
||||
{
|
||||
auto *fileMenu = menuBar()->addMenu(QStringLiteral("Dosya"));
|
||||
QAction *openAction = fileMenu->addAction(QStringLiteral("Senaryo Logu Ac..."));
|
||||
openAction->setObjectName(QStringLiteral("openLogAction"));
|
||||
openAction->setShortcut(QKeySequence::Open);
|
||||
connect(openAction, &QAction::triggered, this, &MainWindow::openTelemetryLog);
|
||||
fileMenu->addSeparator();
|
||||
QAction *quitAction = fileMenu->addAction(QStringLiteral("Cikis"));
|
||||
quitAction->setShortcut(QKeySequence::Quit);
|
||||
connect(quitAction, &QAction::triggered, this, &QWidget::close);
|
||||
|
||||
auto *connectionMenu = menuBar()->addMenu(QStringLiteral("Baglanti"));
|
||||
QAction *listenAction = connectionMenu->addAction(QStringLiteral("Seri Baglantiyi Ac / Kapat"));
|
||||
listenAction->setObjectName(QStringLiteral("toggleSerialAction"));
|
||||
listenAction->setShortcut(QKeySequence(QStringLiteral("Ctrl+K")));
|
||||
connect(listenAction, &QAction::triggered, this, &MainWindow::toggleSerial);
|
||||
QAction *portsAction = connectionMenu->addAction(QStringLiteral("Portlari Yenile"));
|
||||
portsAction->setShortcut(QKeySequence(QStringLiteral("Ctrl+Shift+R")));
|
||||
connect(portsAction, &QAction::triggered, this, [this] {
|
||||
m_portsInitialized = false;
|
||||
refreshSerialPorts();
|
||||
});
|
||||
QAction *rinexAction = connectionMenu->addAction(QStringLiteral("RINEX Guncelle"));
|
||||
rinexAction->setObjectName(QStringLiteral("rinexUpdateAction"));
|
||||
rinexAction->setShortcut(QKeySequence::Refresh);
|
||||
connect(rinexAction, &QAction::triggered, m_rinex, &RinexNavigation::refresh);
|
||||
|
||||
auto *viewMenu = menuBar()->addMenu(QStringLiteral("Gorunum"));
|
||||
QAction *fitAction = viewMenu->addAction(QStringLiteral("Rotayi Haritaya Sigdir"));
|
||||
fitAction->setShortcut(QKeySequence(QStringLiteral("Ctrl+0")));
|
||||
connect(fitAction, &QAction::triggered, m_map, &MapWidget::fitToPoints);
|
||||
QAction *zoomInAction = viewMenu->addAction(QStringLiteral("Yaklastir"));
|
||||
zoomInAction->setShortcut(QKeySequence::ZoomIn);
|
||||
connect(zoomInAction, &QAction::triggered, m_map, &MapWidget::zoomIn);
|
||||
QAction *zoomOutAction = viewMenu->addAction(QStringLiteral("Uzaklastir"));
|
||||
zoomOutAction->setShortcut(QKeySequence::ZoomOut);
|
||||
connect(zoomOutAction, &QAction::triggered, m_map, &MapWidget::zoomOut);
|
||||
|
||||
auto *helpMenu = menuBar()->addMenu(QStringLiteral("Yardim"));
|
||||
QAction *aboutAction = helpMenu->addAction(QStringLiteral("Location Plotter Hakkinda"));
|
||||
connect(aboutAction, &QAction::triggered, this, [this] {
|
||||
QMessageBox::about(this, QStringLiteral("Location Plotter"),
|
||||
QStringLiteral("GNSS telemetri, konum analizi ve RINEX uydu gorunumu\n"
|
||||
"macOS surumu 0.2.0"));
|
||||
});
|
||||
}
|
||||
|
||||
void MainWindow::loadSettings()
|
||||
{
|
||||
QSettings settings;
|
||||
restoreGeometry(settings.value(QStringLiteral("window/geometry")).toByteArray());
|
||||
if (auto *splitter = findChild<QSplitter *>(QStringLiteral("mainSplitter")))
|
||||
splitter->restoreState(settings.value(QStringLiteral("window/mainSplitter")).toByteArray());
|
||||
if (auto *splitter = findChild<QSplitter *>(QStringLiteral("sideSplitter")))
|
||||
splitter->restoreState(settings.value(QStringLiteral("window/sideSplitter")).toByteArray());
|
||||
|
||||
const QString port = settings.value(QStringLiteral("serial/port")).toString();
|
||||
if (!port.isEmpty()) {
|
||||
if (m_portCombo->findText(port) < 0) m_portCombo->addItem(port);
|
||||
m_portCombo->setCurrentText(port);
|
||||
}
|
||||
m_baudCombo->setCurrentText(settings.value(QStringLiteral("serial/baud"), 115200).toString());
|
||||
m_maxPointsSpin->setValue(settings.value(QStringLiteral("telemetry/maxPoints"), 10).toInt());
|
||||
m_autoFollowCheck->setChecked(settings.value(QStringLiteral("map/autoFollow"), true).toBool());
|
||||
m_showTelemetryCheck->setChecked(settings.value(QStringLiteral("map/showTelemetry"), true).toBool());
|
||||
m_showRecentPointsCheck->setChecked(settings.value(QStringLiteral("map/showRecent"), true).toBool());
|
||||
m_showAverageCheck->setChecked(settings.value(QStringLiteral("map/showAverage"), true).toBool());
|
||||
if (auto *elevation = findChild<QDoubleSpinBox *>(QStringLiteral("elevationMaskSpin")))
|
||||
elevation->setValue(settings.value(QStringLiteral("globe/elevationMask"), 10.0).toDouble());
|
||||
if (auto *tabs = findChild<QTabWidget *>(QStringLiteral("mainViewTabs")))
|
||||
tabs->setCurrentIndex(settings.value(QStringLiteral("window/mainTab"), 0).toInt());
|
||||
}
|
||||
|
||||
void MainWindow::saveSettings() const
|
||||
{
|
||||
QSettings settings;
|
||||
settings.setValue(QStringLiteral("window/geometry"), saveGeometry());
|
||||
if (auto *splitter = findChild<QSplitter *>(QStringLiteral("mainSplitter")))
|
||||
settings.setValue(QStringLiteral("window/mainSplitter"), splitter->saveState());
|
||||
if (auto *splitter = findChild<QSplitter *>(QStringLiteral("sideSplitter")))
|
||||
settings.setValue(QStringLiteral("window/sideSplitter"), splitter->saveState());
|
||||
settings.setValue(QStringLiteral("serial/port"), m_portCombo->currentText().trimmed());
|
||||
settings.setValue(QStringLiteral("serial/baud"), selectedBaudRate());
|
||||
settings.setValue(QStringLiteral("telemetry/maxPoints"), m_maxPointsSpin->value());
|
||||
settings.setValue(QStringLiteral("map/autoFollow"), m_autoFollowCheck->isChecked());
|
||||
settings.setValue(QStringLiteral("map/showTelemetry"), m_showTelemetryCheck->isChecked());
|
||||
settings.setValue(QStringLiteral("map/showRecent"), m_showRecentPointsCheck->isChecked());
|
||||
settings.setValue(QStringLiteral("map/showAverage"), m_showAverageCheck->isChecked());
|
||||
if (auto *elevation = findChild<QDoubleSpinBox *>(QStringLiteral("elevationMaskSpin")))
|
||||
settings.setValue(QStringLiteral("globe/elevationMask"), elevation->value());
|
||||
if (auto *tabs = findChild<QTabWidget *>(QStringLiteral("mainViewTabs")))
|
||||
settings.setValue(QStringLiteral("window/mainTab"), tabs->currentIndex());
|
||||
settings.sync();
|
||||
}
|
||||
|
||||
void MainWindow::handleSerialLine(const QString &line)
|
||||
@@ -1084,7 +1211,10 @@ void MainWindow::buildUi()
|
||||
connect(m_clearButton, &QPushButton::clicked, this, &MainWindow::clearPoints);
|
||||
connect(m_openLogButton, &QPushButton::clicked, this, &MainWindow::openTelemetryLog);
|
||||
connect(m_rinexUpdateButton, &QPushButton::clicked, m_rinex, &RinexNavigation::refresh);
|
||||
connect(refreshPortsButton, &QToolButton::clicked, this, &MainWindow::refreshSerialPorts);
|
||||
connect(refreshPortsButton, &QToolButton::clicked, this, [this] {
|
||||
m_portsInitialized = false;
|
||||
refreshSerialPorts();
|
||||
});
|
||||
connect(m_maxPointsSpin, qOverload<int>(&QSpinBox::valueChanged), this,
|
||||
&MainWindow::trimToMaxPoints);
|
||||
connect(m_autoFollowCheck, &QCheckBox::toggled, m_map, &MapWidget::setAutoFollow);
|
||||
|
||||
@@ -18,6 +18,7 @@ class QPushButton;
|
||||
class QSpinBox;
|
||||
class QTableView;
|
||||
class QTimer;
|
||||
class QCloseEvent;
|
||||
|
||||
class MapWidget;
|
||||
class GlobeWidget;
|
||||
@@ -32,6 +33,9 @@ public:
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
bool loadTelemetryLog(const QString &filePath);
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
private slots:
|
||||
void toggleSerial();
|
||||
void clearPoints();
|
||||
@@ -47,6 +51,9 @@ private slots:
|
||||
|
||||
private:
|
||||
void buildUi();
|
||||
void buildMenus();
|
||||
void loadSettings();
|
||||
void saveSettings() const;
|
||||
void updateConnectionControls();
|
||||
void addTelemetryPoint(const ParsedTelemetry &telemetry);
|
||||
void refreshTable();
|
||||
@@ -98,6 +105,7 @@ private:
|
||||
QPlainTextEdit *m_log = nullptr;
|
||||
QTimer *m_locationTimeoutTimer = nullptr;
|
||||
QTimer *m_uiRefreshTimer = nullptr;
|
||||
QTimer *m_portRefreshTimer = nullptr;
|
||||
|
||||
QVector<TelemetryPoint> m_points;
|
||||
QStringList m_pendingLogLines;
|
||||
@@ -108,4 +116,6 @@ private:
|
||||
DopValues m_lastDop;
|
||||
ParsedTelemetry m_pendingTelemetry;
|
||||
bool m_telemetryRefreshPending = false;
|
||||
bool m_portsInitialized = false;
|
||||
QStringList m_lastDetectedPorts;
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "../src/MapWidget.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QAction>
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QDoubleSpinBox>
|
||||
@@ -56,6 +57,9 @@ int main(int argc, char **argv)
|
||||
auto *mainViewTabs = window.findChild<QTabWidget *>(QStringLiteral("mainViewTabs"));
|
||||
auto *sideSplitter = window.findChild<QSplitter *>(QStringLiteral("sideSplitter"));
|
||||
auto *rinexUpdateButton = window.findChild<QPushButton *>(QStringLiteral("rinexUpdateButton"));
|
||||
auto *openLogAction = window.findChild<QAction *>(QStringLiteral("openLogAction"));
|
||||
auto *toggleSerialAction = window.findChild<QAction *>(QStringLiteral("toggleSerialAction"));
|
||||
auto *rinexUpdateAction = window.findChild<QAction *>(QStringLiteral("rinexUpdateAction"));
|
||||
assert(table != nullptr);
|
||||
assert(referenceLatInput != nullptr);
|
||||
assert(referenceLonInput != nullptr);
|
||||
@@ -85,6 +89,9 @@ int main(int argc, char **argv)
|
||||
assert(sideSplitter->orientation() == Qt::Vertical);
|
||||
assert(rinexUpdateButton != nullptr);
|
||||
assert(!rinexUpdateButton->toolTip().isEmpty());
|
||||
assert(openLogAction != nullptr && !openLogAction->shortcut().isEmpty());
|
||||
assert(toggleSerialAction != nullptr && !toggleSerialAction->shortcut().isEmpty());
|
||||
assert(rinexUpdateAction != nullptr && !rinexUpdateAction->shortcut().isEmpty());
|
||||
|
||||
assert(QMetaObject::invokeMethod(map, "referenceLocationSelected", Qt::DirectConnection,
|
||||
Q_ARG(double, 40.25), Q_ARG(double, 29.5)));
|
||||
|
||||
Reference in New Issue
Block a user