Following excellent review of a recent PR, I was asked to update the
conventional string-based Qt connections to the new-style Qt
connections. (https://wiki.qt.io/New_Signal_Slot_Syntax) The old style is:
connect(
sender, SIGNAL( valueChanged( QString, QString ) ),
receiver, SLOT( updateValue( QString ) )
);
My old-style connection is:
connect(ui.timeDiffEdit, SIGNAL(timeChanged(const QTime)), this,
SLOT(timeDiffEditChanged()));
For the new style the specification in the Qt documentation is:
connect(
sender, &Sender::valueChanged,
receiver, &Receiver::updateValue
);
My formulation, reflecting my strong deficiencies in Qt coding but
following the above, for the new connection is:
connect(this,
ui.timeDiffEdit->timeChanged(ui.timeDiffEdit->time()),this,
this->timeDiffEditChanged());
The second parameter needs to be a pointer, for which I used
ui.timeDiffEdit. The timeChanged() signal needs a QTime value, so I
provided that with ui.timeDeffEdit->time(), the actual amount of time by
which the timeDiffEdit has been changed.
The last parameter also needs an address, provided by
this->timeDiffEditChanged() is the response to the signal
fromtimeDiffEdit->timeChanged(). It is a void function and the source of
build problems. The compiler generates an error:
/home/willem/src/subsurface/desktop-widgets/importgps.cpp:30:104: error:
invalid use of void expression
connect(this, ui.timeDiffEdit->timeChanged(ui.timeDiffEdit->time()),
this, this->timeDiffEditChanged());
^
Obviously, accessing the pointer this->timeDiffEditChanged() should not
return a void value. I have no idea of how to respond to this error. Any
suggestions are very welcome. For completeness I attach the code for the
full object to this email.
Kind regards,
willem
--
This message and attachments are subject to a disclaimer.
Please refer to
http://upnet.up.ac.za/services/it/documentation/docs/004167.pdf
<http://upnet.up.ac.za/services/it/documentation/docs/004167.pdf> for
full
details.
// SPDX-License-Identifier: GPL-2.0
#include "ui_importgps.h"
#include "desktop-widgets/importgps.h"
#include "desktop-widgets/locationinformation.h"
#include "core/dive.h"
#include <time.h>
#include <QFileDialog>
#include <QProcess>
#include <QDateTime>
#include <QDate>
#include <stdlib.h>
#include <stdio.h>
#include <QDebug>
#define BUFFER_SIZE 5000
/* Import dive coordinates from a GPS device and synchronise them with the dive profile information
of a dive computer. This file contains the infrastructure to:
1) Read a .GPX file from a GPS system.
2) Find the first gpx trackpoint that follows after the start of the dive.
3) Allow modification of the coordinates dependent on international time zone and
on differences in local time settings between the GPS and the dive computer.
4) Saving the coordinates into the Coordinates text box in the Dive Site Edit panel
and which which causes the map to show the location of the dive site.
The structure coords is used to store critical information. */
ImportGPS::ImportGPS(QWidget *parent, QString fileName, class Ui::LocationInformation *LocationUI) : QDialog(parent),
fileName(fileName), LocationUI(LocationUI)
{
connect(this, ui.timeDiffEdit->timeChanged(ui.timeDiffEdit->time()), this, this->timeDiffEditChanged());
// connect(ui.timeDiffEdit, SIGNAL(timeChanged(const QTime)), this, SLOT(timeDiffEditChanged()));
connect(ui.timeZoneEdit, SIGNAL(timeChanged(const QTime)), this, SLOT(timeZoneEditChanged()));
connect(ui.timezone_backwards, SIGNAL(toggled(bool)), this, SLOT(changeZoneBackwards()));
connect(ui.timezone_forward, SIGNAL(toggled(bool)), this, SLOT(changeZoneForward()));
connect(ui.diff_backwards, SIGNAL(toggled(bool)), this, SLOT(changeDiffBackwards()));
connect(ui.diff_forward, SIGNAL(toggled(bool)), this, SLOT(changeDiffForward()));
connect(ui.GPSbuttonBox, SIGNAL(clicked(QAbstractButton *)), this, SLOT(buttonClicked(QAbstractButton *)));
coords.settingsDiff_offset = 0;
coords.timeZone_offset = 0;
coords.lat = 0;
coords.lon = 0;
pixmapSize = (int) (ui.diveDateLabel->height() / 2);
}
void ImportGPS::buttonClicked(QAbstractButton *button)
{
if (ui.GPSbuttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) {
// Write the coordinates in decimal degree format to the Coordinates QLineEdit of the LocationaInformationWidget UI:
LocationUI->diveSiteCoordinates->setText(QString::number(coords.lat) + ", " + QString::number(coords.lon));
LocationUI->diveSiteCoordinates->editingFinished();
} else {
close();
}
}
// Extract text from the present position in the file until
// the first 'delim' character is encountered.
int ImportGPS::get_substring(FILE *f, char *buf, char delim)
{
int j;
char c;
for (j = 0; (c = fgetc(f)); j++) {
if (c == delim) break;
if (c == EOF) return 1;
buf[j] = c;
if (j >= BUFFER_SIZE)
return 2;
}
buf[j] = 0x0; // terminate the string
return 0;
}
// Find the next occurence of a specified GPX element in the file,
// characerised by a "<xxx " or "<xxx>" character sequence.
// s specifies the name of the element searched for.
// termc is the ending character of the element name search = '>' or ' '.
int ImportGPS::find_xml_element(FILE *f, const char *s, char *buf, char termc)
{
bool match = false;
int j;
char c;
char skipdelim = (termc == ' ') ? '>' : ' ';
do {
for (j = 0; (c = fgetc(f)); j++) { // Find the first '<' character
if (c == '<')
break;
if (c == EOF)
return 1;
if (j >= BUFFER_SIZE)
return 2;
}
for (j = 0; (c = fgetc(f)); j++) { // Extract the xml element name after the '<'
if (c == EOF)
return 1;
buf[j] = c;
if ((c == '>') || (c == ' '))
break;
}
buf[j] = 0x0; // terminate the xml element name
if (!(strcmp(buf, "/trk"))) // End of GPS track found: return EOF
return 1;
if (c == skipdelim) continue; // if wrong delimiter for this element was found
if (strcmp(s, buf)) // Compare xml element name from gpx file with the
match = false; // the element searched for.
else
match = true;
} while (match == false);
return 0;
}
// Extract characters in src from first to last position
// e.g. substr("ascdef",dest,3,5) yields dest = "cde".
char* ImportGPS::substr(const char *src, char *dest, int first, int last)
{
char *substring = dest;
for (int i = first; i <= last && (*src != '\0'); i++) {
*substring = *(src + i); // copy substr from src to dest
substring++;
}
*substring = '\0'; // terminate and return..
return dest; // ..the destination string
}
// Find the coordinates at the time specified in coords.start_dive
// by searching the gpx file "fileName"
int ImportGPS::getCoordsFromFile()
{
struct tm tm1;
time_t when = 0;
double lon, lat;
char buf[BUFFER_SIZE];
char strbuf[50];
int line=0;
int64_t time_offset = coords.settingsDiff_offset + coords.timeZone_offset;
time_t divetime;
bool first_line = true;
bool found = false;
QByteArray local8bitBAString1 = fileName.toLocal8Bit(); // Do these stupid gymnastics to
char * fname = local8bitBAString1.data(); // convert QString to a C string filename
divetime = coords.start_dive;
FILE *f1;
if (!(f1 = fopen(fname, "r"))) {
fprintf(stderr, "File open error %s\n", fname);
return 1;
}
#ifdef GPSDEBUG
struct tm time; // decode the time of start of dive:
utc_mkdate(divetime, &time);
int dyr,dmon,dday,dhr,dmin;
dyr = time.tm_year;
dmon = time.tm_mon;
dday = time.tm_mday;
dhr = time.tm_hour;
dmin = time.tm_min;
#endif
do {
line++; // this is the sequence number of the trkpt xml element processed
// Find next trkpt xml element
if (find_xml_element(f1, "trkpt", buf, ' ')) // find start of next trackpoint
break; // (This function also detects </trk> and signals EOF)
// == Get coordinates: ==
if (get_substring(f1, buf, '"')) // read up to the "lat" attribute
break; // on EOF
if (strcmp(buf, "lat=")) {
fprintf(stderr, "GPX parse error: cannot find latitude (trkpt #%d)\n", line);
return 1;
}
if (get_substring(f1, buf, '"')) // get string with latitude
break; // on EOF
lat = atof(buf); // Convert lat to decimal
if (get_substring(f1, buf, ' ')) // Read past space char
break; // on EOF
if (get_substring(f1, buf, '"')) // Read up to "lon" attribute
break; // on EOF
if (strcmp(buf, "lon=")) {
fprintf(stderr, "GPX parse error: cannot find longitude (trkpt #%d)\n", line);
return 1;
}
if (get_substring(f1, buf, '"')) // get string with longitude
break; // on EOF
lon = atof(buf); // Convert longitude to decimal
// == get time: ==
if (find_xml_element(f1, "time", buf, '>')) // Find the <time> element
break; // on EOF
if (get_substring(f1, buf, '<')) // Read the string containing date/time
break; // on EOF
tm1.tm_year = atoi(substr(buf, strbuf, 0,3)); // Extract the different substrings:
tm1.tm_mon = atoi(substr(buf, strbuf, 5,6))-1;
tm1.tm_mday = atoi(substr(buf, strbuf, 8,9));
tm1.tm_hour = atoi(substr(buf, strbuf, 11,12));
tm1.tm_min = atoi(substr(buf, strbuf, 14,15));
tm1.tm_sec = atoi(substr(buf, strbuf, 17,18));
when = utc_mktime(&tm1) + time_offset;
if (first_line) {
first_line = false;
coords.start_track = when;
}
if ((when > divetime) && (found == false)) { // This GPS time corresponds to the start of the dive
coords.lon = lon; // save the coordinates
coords.lat = lat;
found = true;
}
#ifdef GPSDEBUG
utc_mkdate(when, &time); // print time and coordinates of each of the trkpt elements of the GPX file
fprintf(stderr, " %02d: lat=%f lon=%f timestamp=%ld (%ld) %02d/%02d/%02d %02d:%02d dt=%ld %02d/%02d/%02d %02d:%02d\n", line, lat,
lon, when, time_offset, time.tm_year, time.tm_mon+1, time.tm_mday, time.tm_hour, time.tm_min, divetime, dyr, dmon+1, dday,dhr, dmin );
#endif
} while (true); // This loop executes until EOF causes a break out of the loop
coords.end_track = when;
fclose(f1);
return 0;
}
// Fill the visual elements of the synchronisation panel with information
void ImportGPS::updateUI()
{
struct tm time;
int dive_day, gps_day;
char datestr[50];
QString problemString;
utc_mkdate(coords.start_track, &time); // Display GPS date and start and end times:
gps_day = time.tm_mday;
datestr[0] = 0x0;
strftime(datestr, sizeof(datestr), "%A %d %B ", &time); // GPS date
ui.trackDateLabel->setText("GPS date = " + QString(datestr) + QString::number(time.tm_year));
ui.startTimeLabel->setText(QString::number(time.tm_hour) + ":" + QString::number(time.tm_min)); // track start time
utc_mkdate(coords.end_track, &time);
ui.endTimeLabel->setText(QString::number(time.tm_hour) + ":" + QString::number(time.tm_min)); // track end time
utc_mkdate(coords.start_dive, &time); // Display dive date and start and end times:
dive_day = time.tm_mday;
datestr[0] = 0x0;
strftime(datestr, sizeof(datestr), "%A %d %B ", localtime(&(coords.start_dive))); // dive date
ui.diveDateLabel->setText("Dive date = " + QString(datestr) + QString::number(time.tm_year));
ui.startTimeSyncLabel->setText( QString::number(time.tm_hour) + ":" + QString::number(time.tm_min)); // dive start time
utc_mkdate(coords.end_dive, &time);
ui.endTimeSyncLabel->setText(QString::number(time.tm_hour) + ":" + QString::number(time.tm_min)); // dive end time
// This section implements extensive warnings in case there is not synchronisation between dive and GPS data:
if (gps_day != dive_day)
problemString = "(different dates)";
else
problemString = "";
// Create 3 icons to indicate the quality of the synchrinisation between dive and GPS
QPixmap goodResultIcon (":gps_good_result-icon");
ui.goodIconLabel->setPixmap(goodResultIcon.scaled(pixmapSize,pixmapSize,Qt::KeepAspectRatio));
ui.goodIconLabel->setVisible(false);
QPixmap warningResultIcon (":gps_warning_result-icon");
ui.warningIconLabel->setPixmap(warningResultIcon.scaled(pixmapSize,pixmapSize,Qt::KeepAspectRatio));
ui.warningIconLabel->setVisible(false);
QPixmap badResultIcon (":gps_bad_result-icon");
ui.badIconLabel->setPixmap(badResultIcon.scaled(pixmapSize,pixmapSize,Qt::KeepAspectRatio));
ui.badIconLabel->setVisible(false);
// Show information or warning message as well as synch quality icon
if (coords.start_dive < coords.start_track) {
ui.resultLabel->setStyleSheet("QLabel { color: red;} ");
ui.resultLabel->setText("PROBLEM: Dive started before the GPS track "+ problemString);
ui.badIconLabel->setVisible(true);
} else {
if (coords.start_dive > coords.end_track) {
ui.resultLabel->setStyleSheet("QLabel { color: red;} ");
ui.resultLabel->setText("PROBLEM: Dive started after the GPS track " + problemString);
ui.badIconLabel->setVisible(true);
} else {
if (coords.end_dive > coords.end_track) {
ui.resultLabel->setStyleSheet("QLabel { color: red;} ");
ui.resultLabel->setText("WARNING: Dive ended after the GPS track " + problemString);
ui.warningIconLabel->setVisible(true);
} else {
ui.resultLabel->setStyleSheet("QLabel { color: darkgreen;} ");
ui.resultLabel->setText("Dive coordinates: "+ QString::number(coords.lat) + "S, " + QString::number(coords.lon) + "E");
ui.goodIconLabel->setVisible(true);
}
}
}
}
void ImportGPS::changeZoneForward()
{
coords.timeZone_offset = abs(coords.timeZone_offset);
getCoordsFromFile(); // If any of the time controls are changed
updateUI(); // .. then recalculate the synchronisation
}
void ImportGPS::changeZoneBackwards()
{
if (coords.timeZone_offset > 0)
coords.timeZone_offset = 0 - coords.timeZone_offset;
getCoordsFromFile();
updateUI();
}
void ImportGPS::changeDiffForward()
{
coords.settingsDiff_offset = abs(coords.settingsDiff_offset);
getCoordsFromFile();
updateUI();
}
void ImportGPS::changeDiffBackwards()
{
if (coords.settingsDiff_offset > 0)
coords.settingsDiff_offset = 0 - coords.settingsDiff_offset;
getCoordsFromFile();
updateUI();
}
void ImportGPS::timeDiffEditChanged()
{
coords.settingsDiff_offset = ui.timeDiffEdit->time().hour() * 3600 + ui.timeDiffEdit->time().minute() * 60;
if (ui.diff_backwards->isChecked())
coords.settingsDiff_offset = 0 - coords.settingsDiff_offset;
getCoordsFromFile();
updateUI();
}
void ImportGPS::timeZoneEditChanged()
{
coords.timeZone_offset = ui.timeZoneEdit->time().hour() * 3600;
if (ui.timezone_backwards->isChecked())
coords.timeZone_offset = 0 - coords.timeZone_offset;
getCoordsFromFile();
updateUI();
}
_______________________________________________
subsurface mailing list
[email protected]
http://lists.subsurface-divelog.org/cgi-bin/mailman/listinfo/subsurface