InterfaceGPS 0.1.0
Interface embarquée Qt pour navigation, multimédia, caméra et télémétrie
Chargement...
Recherche...
Aucune correspondance
navigationpage.cpp
Aller à la documentation de ce fichier.
1
8#include "navigationpage.h"
9#include "ui_navigationpage.h"
10#include "telemetrydata.h"
11#include "clavier.h"
12#include <QCompleter>
13#include <QStringListModel>
14#include <QTimer>
15#include <QQmlContext>
16#include <QQuickItem>
17#include <QDebug>
18#include <QJsonDocument>
19#include <QJsonArray>
20#include <QVariant>
21
23 : QWidget(parent), ui(new Ui::NavigationPage)
24{
25 // Cette page joue le rôle de contrôleur : elle coordonne QML, requêtes réseau et clavier virtuel.
26 ui->setupUi(this);
27
28 // --- CONFIGURATION DE L'AUTOCOMPLÉTION ---
29 m_suggestionsModel = new QStringListModel(this);
30 m_searchCompleter = new QCompleter(m_suggestionsModel, this);
31 m_searchCompleter->setCaseSensitivity(Qt::CaseInsensitive);
32 m_searchCompleter->setFilterMode(Qt::MatchContains);
33 m_searchCompleter->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
34 ui->editSearch->setCompleter(m_searchCompleter);
35
36 // Installation du filtre d'événement pour intercepter le clic sur la barre de recherche
37 ui->editSearch->installEventFilter(this);
38
39 // Timer de Debounce : attend 300ms après la dernière touche frappée avant de lancer la recherche.
40 // Cela évite d'épuiser le quota de requêtes API Mapbox/Here quand on tape vite.
41 m_suggestionDebounceTimer = new QTimer(this);
42 m_suggestionDebounceTimer->setSingleShot(true);
43 m_suggestionDebounceTimer->setInterval(800);
44
45 // Connexions pour l'autocomplétion
46 connect(m_searchCompleter, static_cast<void (QCompleter::*)(const QString &)>(&QCompleter::activated),
47 this, &NavigationPage::onSuggestionChosen);
48
49 connect(m_suggestionDebounceTimer, &QTimer::timeout,
50 this, &NavigationPage::triggerSuggestionsSearch);
51
52 connect(ui->editSearch, &QLineEdit::textEdited, this, [this](const QString &text) {
53 if (!m_ignoreTextUpdate && text.length() >= 2) {
54 m_suggestionDebounceTimer->start(); // Relance le timer à chaque nouvelle lettre
55 }
56 });
57
58 // --- CONFIGURATION DE LA CARTE QML ---
59 // QQuickWidget héberge la carte QML dans la hiérarchie QWidget existante.
60 m_mapView = new QQuickWidget(this);
61
62 // Récupération des clés API depuis les variables d'environnement
63 QString mapboxKey = QString::fromLocal8Bit(qgetenv("MAPBOX_API_KEY")).trimmed();
64
65 // Injection des clés API dans le contexte QML pour qu'elles soient lisibles par la carte
66 m_mapView->rootContext()->setContextProperty("mapboxApiKey", mapboxKey);
67 m_mapView->setResizeMode(QQuickWidget::SizeRootObjectToView);
68
69 // Fonction lambda pour lier les signaux QML aux slots C++ une fois la carte chargée
70 auto setupQmlConnections = [this]() {
71 QObject* root = m_mapView->rootObject();
72 if (!root) return;
73 connect(root, SIGNAL(routeInfoUpdated(QString,QString)), this, SLOT(onRouteInfoReceived(QString,QString)));
74 connect(root, SIGNAL(suggestionsUpdated(QString)), this, SLOT(onSuggestionsReceived(QString)));
75 };
76
77 // Attendre que le QQuickWidget soit prêt avant de faire les connexions QML
78 connect(m_mapView, &QQuickWidget::statusChanged, this, [setupQmlConnections](QQuickWidget::Status status){
79 if (status == QQuickWidget::Ready) setupQmlConnections();
80 });
81
82 // Chargement du fichier QML et ajout au layout de l'interface
83 m_mapView->setSource(QUrl("qrc:/map.qml"));
84 ui->mapLayout->addWidget(m_mapView);
85
86 // --- CONNEXION DES BOUTONS DE CONTRÔLE DE LA CARTE ---
87 connect(ui->btnZoomIn, &QPushButton::clicked, this, [this](){
88 if (m_mapView && m_mapView->rootObject()) {
89 double z = m_mapView->rootObject()->property("carZoom").toDouble();
90 m_mapView->rootObject()->setProperty("carZoom", z + 1);
91 }
92 });
93 connect(ui->btnZoomOut, &QPushButton::clicked, this, [this](){
94 if (m_mapView && m_mapView->rootObject()) {
95 double z = m_mapView->rootObject()->property("carZoom").toDouble();
96 m_mapView->rootObject()->setProperty("carZoom", z - 1);
97 }
98 });
99 connect(ui->btnCenter, &QPushButton::clicked, this, [this](){
100 if(m_mapView && m_mapView->rootObject()) {
101 QMetaObject::invokeMethod(m_mapView->rootObject(), "recenterMap");
102 }
103 });
104 connect(ui->btnToggleSearch, &QPushButton::clicked, this, [this](){
105 setSearchControlsVisible(!ui->editSearch->isVisible());
106 });
107 connect(ui->btnStopRoute, &QPushButton::clicked, this, [this](){
108 if (m_mapView && m_mapView->rootObject()) {
109 QMetaObject::invokeMethod(m_mapView->rootObject(), "stopNavigation");
110 }
111 setSearchControlsVisible(true);
112 });
113 connect(ui->btnSearch, &QPushButton::clicked, this, [this](){
114 requestRouteForText(ui->editSearch->text());
115 });
116}
117
119 delete ui;
120}
121
122bool NavigationPage::eventFilter(QObject *obj, QEvent *event)
123{
124 // On intercepte le focus de la barre de recherche (clic de souris)
125 // pour imposer le clavier tactile applicatif au lieu du clavier système Windows/Linux.
126 if (obj == ui->editSearch && event->type() == QEvent::MouseButtonPress) {
127 openVirtualKeyboard();
128 return true; // L'événement est consommé
129 }
130 return QWidget::eventFilter(obj, event);
131}
132
133void NavigationPage::openVirtualKeyboard()
134{
135 // Le clavier est modal (bloquant) pour éviter la coexistence avec un clavier système Qt.
136 Clavier clavier(this);
137 m_currentClavier = &clavier;
138
139 clavier.setInitialText(ui->editSearch->text());
140
141 // Permet de mettre à jour la barre de recherche en temps réel pendant la frappe sur le clavier custom
142 connect(&clavier, &Clavier::textChangedExternally, this, [this](const QString &text){
143 ui->editSearch->setText(text);
144 triggerSuggestionsSearch(); // Relance la recherche de suggestions dynamiques
145 });
146
147 // Si l'utilisateur valide sa saisie (Touche Entrée sur le clavier)
148 if (clavier.exec() == QDialog::Accepted) {
149 QString res = clavier.getText();
150 ui->editSearch->setText(res);
151 requestRouteForText(res);
152 }
153
154 m_currentClavier = nullptr;
155}
156
157void NavigationPage::setSearchControlsVisible(bool visible)
158{
159 ui->editSearch->setVisible(visible);
160 ui->btnSearch->setVisible(visible);
161 ui->btnToggleSearch->setText("🔍");
162
163 if (visible) {
164 ui->editSearch->setFocus();
165 }
166}
167
168void NavigationPage::onSuggestionsReceived(const QString& jsonSuggestions) {
169 // Les suggestions renvoyées par QML (Mapbox) arrivent au format JSON.
170 // On les décode pour alimenter :
171 // 1. La liste du Completer Qt (si on tape au clavier physique)
172 // 2. La liste du clavier tactile custom
173
174 QJsonDocument doc = QJsonDocument::fromJson(jsonSuggestions.toUtf8());
175 QJsonArray arr = doc.array();
176 QStringList suggestions;
177
178 for(const auto& val : arr) {
179 suggestions << val.toString();
180 }
181
182 m_suggestionsModel->setStringList(suggestions);
183
184 // Transmission des suggestions au clavier virtuel s'il est ouvert
185 if (m_currentClavier) {
186 m_currentClavier->displaySuggestions(suggestions);
187 }
188}
189
191 m_t = t;
192 if(!m_t) return;
193
194 // Fonction lambda pour synchroniser les données GPS C++ vers les propriétés de la carte QML
195 auto refresh = [this](){
196 emit telemetryRefreshRequested(m_t->lat(), m_t->lon(), m_t->heading(), m_t->speedKmh());
197 if(m_mapView && m_mapView->rootObject()){
198 m_mapView->rootObject()->setProperty("carLat", m_t->lat());
199 m_mapView->rootObject()->setProperty("carLon", m_t->lon());
200 m_mapView->rootObject()->setProperty("carHeading", m_t->heading());
201 m_mapView->rootObject()->setProperty("carSpeed", m_t->speedKmh());
202 }
203 };
204
205 // Connexion aux signaux de télémétrie pour une mise à jour en temps réel
206 connect(m_t, &TelemetryData::latChanged, this, refresh);
207 connect(m_t, &TelemetryData::lonChanged, this, refresh);
208 connect(m_t, &TelemetryData::headingChanged, this, refresh);
209 connect(m_t, &TelemetryData::speedKmhChanged, this, refresh);
210
211
212 refresh(); // Premier appel pour initialiser la carte avec les valeurs actuelles
213}
214
215void NavigationPage::requestRouteForText(const QString& destination) {
216 QString trimmed = destination.trimmed();
217 if (trimmed.isEmpty()) return;
218
219 emit routeSearchRequested(trimmed);
220 setSearchControlsVisible(false);
221
222 if (!m_mapView || !m_mapView->rootObject()) return;
223
224 // Appel d'une fonction Javascript/QML directement depuis le C++
225 QMetaObject::invokeMethod(m_mapView->rootObject(), "searchDestination",
226 Q_ARG(QVariant, trimmed));
227}
228
229void NavigationPage::onSuggestionChosen(const QString& suggestion) {
230 // Bloque temporairement l'événement de modification de texte pour éviter
231 // que la sélection d'une suggestion ne relance une nouvelle recherche de suggestions (boucle infinie).
232 m_ignoreTextUpdate = true;
233 ui->editSearch->setText(suggestion);
234 m_ignoreTextUpdate = false;
235
236 requestRouteForText(suggestion);
237}
238
239void NavigationPage::triggerSuggestionsSearch() {
240 QString query = ui->editSearch->text().trimmed();
241
242 // Inutile de chercher pour moins de 3 caractères
243 if (query.size() < 3) return;
244
245 emit suggestionsSearchRequested(query);
246
247 if (m_mapView && m_mapView->rootObject()) {
248 // Envoie la requête au script QML pour interrogation de l'API cartographique
249 QMetaObject::invokeMethod(m_mapView->rootObject(), "requestSuggestions", Q_ARG(QVariant, query));
250 }
251}
Interface de saisie tactile sur mesure pour l'application embarquée. S'ouvre sous forme de boîte de d...
Definition clavier.h:29
void textChangedExternally(const QString &text)
Émis à chaque fois qu'une touche modifie le texte. Permet à l'application en arrière-plan (ex: Naviga...
void displaySuggestions(const QStringList &suggestions)
Met à jour la liste déroulante des suggestions au-dessus du clavier.
Definition clavier.cpp:358
void setInitialText(const QString &text)
Pré-remplit la barre de saisie à l'ouverture du clavier.
Definition clavier.cpp:353
Contrôleur de la page de navigation GPS. Héberge la carte (codée en QML) au sein de l'interface C++....
void routeSearchRequested(const QString &destination)
Demande le calcul d'un itinéraire vers une destination textuelle.
void suggestionsSearchRequested(const QString &query)
Demande une liste de suggestions d'adresses pour l'autocomplétion.
~NavigationPage()
Destructeur.
void telemetryRefreshRequested(double lat, double lon, double heading, double speedKmh)
Transporte un snapshot télémétrique vers la couche cartographique.
void bindTelemetry(TelemetryData *t)
Connecte le bus de télémétrie à la carte.
bool eventFilter(QObject *obj, QEvent *event) override
Filtre d'événements global pour ce widget. Utilisé ici pour intercepter les clics sur la barre de rec...
NavigationPage(QWidget *parent=nullptr)
Constructeur de la page de navigation.
Classe représentant les données en temps réel du véhicule. Cette classe hérite de QObject et centrali...
void latChanged()
Notifie une mise à jour de latitude.
void lonChanged()
Notifie une mise à jour de longitude.
double lat() const
Retourne la latitude actuelle en degrés.
double heading() const
Retourne le cap actuel du véhicule en degrés (0 = Nord).
double lon() const
Retourne la longitude actuelle en degrés.
void headingChanged()
Notifie une mise à jour de cap/heading.
void speedKmhChanged()
Notifie une mise à jour de la vitesse véhicule (km/h).
double speedKmh() const
Retourne la vitesse actuelle en km/h.
Rôle architectural : Clavier virtuel propriétaire utilisé par les écrans de saisie.
Rôle architectural : Façade Widget de la navigation cartographique.
Rôle architectural : Modèle central de télémétrie partagé entre les modules C++ et l'interface QML.