Solved File IO and/or integration with C++
-
I've been spinning my wheels trying to do some (sort of simple) file IO in an example app I've worked on trying to become more familiar with QML. The ultimate goal is to read/write simple formatted file. Maybe simple text, maybe tabulated numeric values, maybe CSV. Unfortunately, neither of two alternatives has yet worked out.
-
Is is possible to do such simple file IO directly with any standard QML modules? If such exists, I'd greatly appreciate pointers.
-
How do I return data from C++ functions so that QML can use them directly?
I am fairly experienced with Qt and C++ and have a collection of very useful tools to accomplish such IO tasks. However, I have been unsuccessful getting that data into the QML app. I CAN retrieve data from a file as simple list of string data (specifically, read each line of the file into a QString, accumulate these lines into a QStringList, which is returned from the read function. My QML app interprets that coorectly when that QStringList is read as a QML var variable. Presumably, I could implement a parser with QML tools to re-interpret that list of strings into a list of data values, but that seems pretty klunky.
As example of what I really might want, the data file might be well represented as a QGeoPath. I'd rather return that to QML as a QML geopath, but I have completely failed at that.
What might I be missing that would let me share data between C++ and QML is something more useful that a list of strings?
Any pointers appreciated
-
-
Hi,
Are you thinking about something like a QML QFile wrapper like this one ?
-
Thanks.
Have you actually used this module? Two things jumped out immediately.
-
I will need a bit of additional info to get this working. I was able to create the qml-files library and dll, but haven't been able to make an executable from any of the various test*.qml files.
-
Once I get it working, it isn't obvious that it gets me any closer to my goal. Specifically, As near as I can tell, all it does "out of the box" is let me read files into QString data - I can already do that. I don't see where it gives any useful hints how to read and return any other more structured sorts of data. From the example mentioned in my original post, Qt/QML documentations implies that conversion between QGeoPath and geopath should be seemless and automatic - but it apparently isn't. If this module has anything comparable that I could use for a model, I don't find it. If it is there, I could probably follow it.
-
-
-
No, it was just an example taken to ensure we were talking about the same thing.
-
Can you explain a bit more how your application is working ? That should help implementing the C++ class needed.
-
-
This is just one specific example of a more general expectation. And my intended use is entirely personal and indented for "educational" value.
I have been using a hand-held GPS for many years and have a significant collection of recorded tracks, marked waypoints, intended routes for hiking, etc. Unfortunately, there is no longer commercial software available that lets me easily show this (and other) data on a map. I have managed various klunky work-arounds, but thought it would be fun to see if I could use Qt to implement a simple app for this purpose. The standard example location/mapviewer seemed like a good starting point. It was straightforward to change it to do everything I wanted - except read my library of prior GPS data. I had already assempled all the pieces to parse GPS date (really just pretty simple XML files).
It was simple to hard-code coordinates from tracks/routes/waypoints into the example. For example, I implemented a track as a MapPolyline - that part was easy and worked fine. Then, I expected to simply read an XML file in C++ (I already do that correctly) as a QGeoPath and return that to QML. I implemented a c++ routine, say readPath, with a function, say getPath(fname), that read a QGeoPath from file fname (that part works). I had expected to use that in a QML function like (where "geopath" is a
geopath newpath = readPath.getPath(fname)
(the QML documentation advertises geopath as a Basic QML Type)
I would have then used a MapPolyline to display that path. Perhaps something like
MapPolyline somepath
somepath..setPath(newpath)
map.addMapItem(somepath)where "map" is an already properly-displayed map. (Yes, I know the above is a bit abbreviated, that the actual code is somewhat more involved. I copied/modified the relevant parts of mapview to make it work my hard-coded MapPolyline)
I have tried quite a number of variations on this theme, and all have worked EXCEPT for the step
geopath newpath = readPath.getPath(fname)
QML does not seem to know what a geopath is.
If I instead do
var newpath = readPath.getPath(fname)
QML doesn't complain, but the wrapper code above doesn't work correctly anyway. Nor does the debugger know how to parse newpath. It is parked as "not empty" and "valid", but it doesn't know anything about its contents.
If the C++ module includes a function getQStringList(fname), which returns the contents of fname as a QStringList, and I then use
var newpath = readPath.getQStringLIst(fname)
then the debugger reports correctly the string data from fname - but only as strings, not as useful coordinate data.
Does this help at all to give a sense of what I'm trying to accomplish and how I've gone about accomplishing it?
Thanks in advance.
-
Interesting project !
The behaviour looks indeed strange. Did you try to implement a simple object with a method returning a fixed QGeoPath to ensure it's (or not) working as expected ?
-
Not sure I know exactly what you mean by "simple object". My C++ object couldn't be much simpler. The key function is:
QGeoPath ReadFile::getGeoPath()
{
QGeoPath geopath;if (!infile.exists()) return geopath;
tRecordList inlist = infile.getWordlistList();
QGeoCoordinate coord;
for (size_t i=1 ; i<inlist.size() ; i++)
{
double lat = std::stod(inlist.at(i).at(0));
double lon = std::stod(inlist.at(i).at(1));
double alt = std::stod(inlist.at(i).at(2));
coord = QGeoCoordinate(lat, lon, alt);
geopath.addCoordinate(coord);
}QList<QGeoCoordinate> list = geopath.path();
QGeoCoordinate first = list.front();
QGeoCoordinate last = list.back();return geopath;
}
The input file I read is just a CSV file of (latitude, longitude, altidude) values (I could have used a module to read the direct GPS XML files, but that just complicates everything for present purposes).
Two clarifications that follow from the fact that most of my coding is NOT within Qt, so I primarily use standard c++ vectors and strings for everything that needs to co-exist with non-Qt modules. 1) tRecordList is really a vector of string vectors. 2) the function infile.getWordlistList() just turns each line of the input file into a vector of strings, then stores all lines in a vector.
It's pretty clear how I assemble the QGeoPath. I stuck in the last three operative lines just to make it easier to verify that I read in the data and assembled the path as desired.
The relevant part of the QML module is also really simple:
var mypath = readfile.getGeoPath()
When I inspect path in the Qt debugger, what I see is
mypath object
isEmpty false boolean
isValid true boolean
path object
type 3 number
width 0 numberIt appears that mypath is a valid object and contains an internal path (actually geopath). But I don't know how to inspect that internal path object.
So short answer is (two part) 1) I have good evidence the c++ module works correctly but 2) I don't seem to have the tools required to determine if QML interprets that correctly.
-
As a complete and total aside, I presently do have a really icky workaround. Instead of reading a geopath directly, I can read each line in the datafile individually and return that as a QStringList, which QML understands. I can then rely on QML to take care of the string to number conversion and assemble the path with a series of
point = QtPositioning.coordinate(myLat, myLon, myAlt)
where myLat, etc. is just the appropriate string from the QStringList.
This really offends my programming sensibilities, but allows me to continue moving forward until I figure out a better way to proceed.
-
If you add
import QtPositioning 5.11
you'll have the support for QGeoPath into your QML code.Here is a small example:
main.cpp:
#include <QGuiApplication> #include <QQuickView> #include <QGeoPath> #include <QQmlEngine> #include <QQmlContext> class GeoStuff : public QObject { Q_OBJECT public: Q_INVOKABLE QGeoPath geoPath() const { QList<QGeoCoordinate> coordinates{QGeoCoordinate(1.0, 2.0)}; return QGeoPath(coordinates); } }; int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQuickView view; GeoStuff gs; view.engine()->rootContext()->setContextProperty("gs", &gs); view.setSource(QUrl("main.qml")); view.show(); return app.exec(); } #include "main.moc"
main.qml:
import QtQuick 2.0 import QtPositioning 5.11 Text { width: 100; height: 100 text: "Test: " + gs.geoPath() }
main.qrc
<!DOCTYPE RCC><RCC version="1.0"> <qresource> <file>main.qml</file> </qresource> </RCC>
test_geopath.pro
###################################################################### # Automatically generated by qmake (3.1) Thu May 24 23:23:52 2018 ###################################################################### TEMPLATE = app TARGET = test_geopath INCLUDEPATH += . QT += positioning quick # The following define makes your compiler warn you if you use any # feature of Qt which has been marked as deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS # You can also make your code fail to compile if you use deprecated APIs. # In order to do so, uncomment the following line. # You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 # Input SOURCES += main.cpp RESOURCES += main.qrc
-
Sorry for delayed response. It took me a while to notice your suggestion, which seems to have addressed my issue.
By the way, does this rely on new capabilities in QtPositioning 5.11? While I no longer have a copy of my initial attempts to do this, it looks remarkably similar to what I tried from the outset. At least, I don't see any obvious differences.
Anyway, this works according to my expectations and completely addresses the issue
Thanks!
-
I used 5.11 because it's what I had on my machine. It will likely work with older versions.