Qt 6.2: QMediaPlayer & QByteArray - c++

Good day.
Has anyone tried QMediaPlayer in Qt 6.2 already?
I'm trying this code, but Media Status always remains as "NoMedia" and no any sound :).
Full test project: https://github.com/avttrue/MediaPlayerTest
#include "mainwindow.h"
#include <QDebug>
#include <QBuffer>
#include <QFile>
#include <QAudioOutput>
#include <QMediaPlayer>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QFile file("../test/Bankrobber.mp3");
if(!file.open(QIODevice::ReadOnly))
qDebug() << "File not opened";
qDebug() << "File size:" << file.size(); // File size: 11181085
QByteArray ba = file.readAll();
qDebug() << "ByteArray size:" << ba.size(); // ByteArray size: 11181085
QBuffer* buffer = new QBuffer(this);
buffer->setData(ba);
if(!buffer->open(QIODevice::ReadOnly))
qDebug() << "Buffer not opened";
qDebug() << "Buffer size:" << buffer->size(); // Buffer size: 11181085
buffer->seek(qint64(0));
auto audioOutput = new QAudioOutput(this);
auto player = new QMediaPlayer(this);
player->setAudioOutput(audioOutput);
audioOutput->setVolume(50);
player->setSourceDevice(buffer);
qDebug() << "Device:" << player->sourceDevice(); // Device: QBuffer(0x563180493020)
QObject::connect(player, &QMediaPlayer::mediaStatusChanged,
[=](QMediaPlayer::MediaStatus status)
{ qDebug() << "MediaStatus:" << player->mediaStatus() << "|" << status; });
QObject::connect(player, &QMediaPlayer::errorOccurred,
[=](QMediaPlayer::Error error)
{ qDebug() << "Error:" << player->errorString() << "|" << error; });
QObject::connect(player, &QMediaPlayer::playbackStateChanged,
[=](QMediaPlayer::PlaybackState state)
{ qDebug() << "PlaybackState:" << player->playbackState() << "|" << state; });
player->play();
qDebug() << "MediaStatus:" << player->mediaStatus(); // MediaStatus: QMediaPlayer::NoMedia
}

As the docs points out:
void QMediaPlayer::setSourceDevice(QIODevice *device, const QUrl
&sourceUrl = QUrl())
Sets the current source device.
The media data will be read from device. The sourceUrl can be provided
to resolve additional information about the media, mime type etc. The
device must be open and readable.
For macOS the device should also be seek-able.
Note: This function returns immediately after recording the specified
source of the media. It does not wait for the media to finish loading
and does not check for errors. Listen for the mediaStatusChanged() and
error() signals to be notified when the media is loaded, and if an
error occurs during loading.
(emphasis mine)
QMediaPlayer does not know how to deduce the file format so it does not load it. The solution is to point out that it is an mp3:
player->setSourceDevice(buffer, QUrl("foo.mp3"));

The function setSourceDevice() you are using isn't doing what you think?
Maybe you wanted setSource() instead?
Qt has great documentation:
https://doc.qt.io/qt-6/qmediaplayer.html#setSourceDevice
Even good examples:
player = new QMediaPlayer;
audioOutput = new QAudioOutput;
player->setAudioOutput(audioOutput);
connect(player, SIGNAL(positionChanged(qint64)), this, SLOT(positionChanged(qint64)));
player->setSource(QUrl::fromLocalFile("/Users/me/Music/coolsong.mp3"));
audioOutput->setVolume(50);
player->play();
Ref.
https://doc.qt.io/qt-6/qmediaplayer.html#details

May be this is as variant, but i think it not good:
QTemporaryFile tfile;
if (!tfile.open())
qDebug() << "TemporaryFile not opened";
else
{
qDebug() << "TemporaryFile writed:" << tfile.write(ba);
if(tfile.size() != ba.size())
qDebug() << "TemporaryFile not complited";
else
player->setSource(QUrl::fromLocalFile(tfile.fileName()));
}

Related

Qt cannot write to file. File exists, isn't open, "Unknown error" (Windows OS)

Would really appreciate your help. I'm new to Qt and C++.
I managed to get this working previously, but for some reason am no longer able to do so. I haven't touched anything to do with the writing of files, but all functions pertaining to file writing no longer seem to function. Here's an example of one of the simpler write functions:
#include <QStringList>
#include <QDir>
#include <QFile>
#include <QString>
#include <QTextStream>
void HandleCSV::writeToPIDCSV(UserAccount newUser)
{
// Storing in userPID db
QString filePath = returnCSVFilePath("dbPID");
qDebug() << "File path passsed to writeToPIDCSV is " << filePath;
// Open CSV filepath retrieved from associated dbName
QFile file(filePath);
if (!file.open(QIODevice::ReadWrite | QIODevice::Append))
{
qDebug() << file.isOpen() << "error " << file.errorString();
qDebug() << "File exists? " << file.exists();
qDebug() << "Error message: " << file.error();
qDebug() << "Permissions err: " << file.PermissionsError;
qDebug() << "Read error: " << file.ReadError;
qDebug() << "Permissions before: " << file.permissions();
// I tried setting permissions in case that was the issue, but there was no change
file.setPermissions(QFile::WriteOther);
qDebug() << "Permissions after: " << file.permissions();
}
// if (file.open(QIODevice::ReadWrite | QIODevice::Append))
else
{
qDebug() << "Is the file open?" << file.isOpen();
// Streaming info back into db
QTextStream stream(&file);
stream << newUser.getUID() << "," << newUser.getEmail() << "," << newUser.getPassword() << "\n";
}
file.close();
}
This gets me the following output when run:
File path passsed to writeToPIDCSV is ":/database/dummyPID.csv"
false error "Unknown error"
File exists? true
Error message: 5
Permissions err: 13
Read error: 1
Permissions before: QFlags(0x4|0x40|0x400|0x4000)
Permissions after: QFlags(0x4|0x40|0x400|0x4000)
The file clearly exists and is recognised when run, but for some reason file.isOpen() is false, and the permissions show that the user only has read (not write) permissions that are unaffected by permission settings.
Would someone have a clue as to why this is happening?
Many thanks in advance
Update
Thanks #chehrlic and #drescherjm - I didn't realise that I can only read but not write to my resource file!
Using QStandardPaths allows me to write to my AppDataLocation. I've included the code below for others who might encounter similar issues, adapted from https://stackoverflow.com/a/32535544/9312019
#include <QStringList>
#include <QDir>
#include <QFile>
#include <QString>
#include <QTextStream>
#include <QStandardPaths>
void HandleCSV::writeToPIDCSV(UserAccount newUser)
{
auto path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
if (path.isEmpty()) qFatal("Cannot determine settings storage location");
QDir d{path};
QString filepath = returnCSVFilePath("dbPID");
if (d.mkpath(d.absolutePath()) && QDir::setCurrent(d.absolutePath()))
{
qDebug() << "settings in" << QDir::currentPath();
QFile f{filepath};
if (f.open(QIODevice::ReadWrite | QIODevice::Append))
{
QTextStream stream(&f);
stream << newUser.getUserFirstName() << ","
<< newUser.getUserIDNumber() << "\n";
}
}
}
Am now testing to make this work with my Read functions.
If there's any way to be able to write to the Qt program's folder (or build folder), I'd really appreciate it!
You can not write to a Qt resource. If you want to update/write to a file you should put the file into a writeable filesystem. To e.g. place it in the appdata folder you can use QStandardPaths::writeableLocation(QStandardPaths::ApplicationsLocation)

On Fedora using Qt 5.9.4, I'm unable to simultaneously record and play audio at the same time

I'm trying to write a program in Qt that simultaneously records audio from a microphone and plays it back at the same time. I'm using Qt 5.9.4 and I'm on Fedora 29 (can't update to latest version as our production environment is Fedora 29 -- can't update it, have already asked boss).
I have some barebones code written, as you can see below. But everytime I run the program, I get the following error message:
using null output device, none available
using null input device, none available
I've installed every qt5* package. I have alsa-utils and pulse audio installed as well.
I have also looked at these which more or less helped me but did not solve my problem:
Qt - how to record and play sound simultaneously
https://forum.qt.io/topic/10399/how-to-record-and-play-sound-simultaneously/5
https://www.qtcentre.org/threads/45300-How-to-record-and-play-sound-simultaneously
Qt - No audio output device - Yocto/poky
Qt + conan = using null output device, none available
I don't know if this is a fedora related issue or a Qt related issue. Please help!
myaudiorecorder.h:
#ifndef MYAUDIORECORDER_H
#define MYAUDIORECORDER_H
#include <QAudioFormat>
#include <QAudioDeviceInfo>
#include <QTextStream>
#include <QAudioInput>
#include <QAudioOutput>
#include <QObject>
class MyAudioRecorder : public QObject
{
Q_OBJECT
public:
MyAudioRecorder();
QAudioFormat formatIn;
QAudioFormat formatOut;
QAudioInput *m_audioInput;
QAudioOutput *m_audioOutput;
QAudioDeviceInfo m_InputDevice;
QAudioDeviceInfo m_OutputDevice;
QIODevice *m_input;
QIODevice *m_output;
QAudioDeviceInfo deviceIn;
QAudioDeviceInfo deviceOut;
void getFormat();
void createAudioInput();
void createAudioOutput();
void beginAudio();
};
#endif // MYAUDIORECORDER_H
myaudiorecorder.cpp:
#include "myaudiorecorder.h"
MyAudioRecorder::MyAudioRecorder() {
getFormat();
createAudioInput();
createAudioOutput();
}
void MyAudioRecorder::getFormat(){
formatIn.setSampleSize(8);
formatIn.setCodec("audio/pcm");
formatIn.setByteOrder(QAudioFormat::LittleEndian);
formatIn.setSampleType(QAudioFormat::UnSignedInt);
deviceIn = QAudioDeviceInfo::availableDevices(QAudio::AudioInput).at(1);
if(!deviceIn.isFormatSupported(formatIn)){
QTextStream(stdout) << " default formatIn not supported " << endl;
formatIn = deviceIn.nearestFormat(formatIn);
} else {
QTextStream(stdout) << " default formatIn supported " << endl;
}
deviceOut = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput).at(0);
if(!deviceOut.isFormatSupported(formatOut)) {
QTextStream(stdout) << "1. default formatOut not supported " << endl;
formatOut = deviceOut.nearestFormat(formatOut);
}
}
void MyAudioRecorder::createAudioInput(){
m_audioInput = new QAudioInput(m_InputDevice, formatIn, 0);
}
void MyAudioRecorder::createAudioOutput(){
m_audioOutput = new QAudioOutput(m_OutputDevice, formatOut, 0);
}
void MyAudioRecorder::beginAudio(){
m_output = m_audioOutput->start();
m_input = m_audioInput->start();
}
void MyAudioRecorder::beginAudio(){
m_output = m_audioOutput->start();
m_audioInput->start(m_output);
//Above should do the trick but do check the volume, state and error if any:
qDebug() << "m_audioInput: volume=" << m_audioInput->volume()
<< ", state=" << m_audioInput->state()
<< ", error=" << m_audioInput->error();
qDebug() << "m_audioOutput: volume=" << m_audioOutput->volume()
<< ", state=" << m_audioOutput->state()
<< ", error=" << m_audioOutput->error();
}
you need copy qt Qt/5.9.x/mingw73_64/plugins/audio to you binary directory,not copy qtaudio_windows.dll ,is copy audio folder

how to get latitude/longitude from geo address using Qt c++ QGeoServiceProvider?

I am trying to use the osm api via a QGeoServiceProvider and QGeoCodingManager to derive the latitude and longitude from a given physical address.
Thereby I stumbled upon this Question, which seems to do exactly what I need.
However I want to implement this in a qt gui application as a separate function and I don' t understand what is happening in the connect.
Could someone please explain or how I can modify it to get it to work in a function ?
cout << "Try service: " << "osm" << endl;
// choose provider
QGeoServiceProvider qGeoService("osm");
QGeoCodingManager *pQGeoCoder = qGeoService.geocodingManager();
if (!pQGeoCoder) {
cerr
<< "GeoCodingManager '" << "osm"
<< "' not available!" << endl;
}
QLocale qLocaleC(QLocale::C, QLocale::AnyCountry);
pQGeoCoder->setLocale(qLocaleC);
// build address
QGeoAddress qGeoAddr;
qGeoAddr.setCountry(QString::fromUtf8("Germany"));
qGeoAddr.setPostalCode(QString::fromUtf8("88250"));
qGeoAddr.setCity(QString::fromUtf8("Weingarten"));
qGeoAddr.setStreet(QString::fromUtf8("Heinrich-Hertz-Str. 6"));
QGeoCodeReply *pQGeoCode = pQGeoCoder->geocode(qGeoAddr);
if (!pQGeoCode) {
cerr << "GeoCoding totally failed!" << endl;
}
cout << "Searching..." << endl;
QObject::connect(pQGeoCode, &QGeoCodeReply::finished,
[&qGeoAddr, pQGeoCode](){
cout << "Reply: " << pQGeoCode->errorString().toStdString() << endl;
switch (pQGeoCode->error()) {
#define CASE(ERROR) \
case QGeoCodeReply::ERROR: cerr << #ERROR << endl; break
CASE(NoError);
CASE(EngineNotSetError);
CASE(CommunicationError);
CASE(ParseError);
CASE(UnsupportedOptionError);
CASE(CombinationError);
CASE(UnknownError);
#undef CASE
default: cerr << "Undocumented error!" << endl;
}
if (pQGeoCode->error() != QGeoCodeReply::NoError) return;
// eval. result
QList<QGeoLocation> qGeoLocs = pQGeoCode->locations();
cout << qGeoLocs.size() << " location(s) returned." << endl;
for (QGeoLocation &qGeoLoc : qGeoLocs) {
qGeoLoc.setAddress(qGeoAddr);
QGeoCoordinate qGeoCoord = qGeoLoc.coordinate();
cout
<< "Lat.: " << qGeoCoord.latitude() << endl
<< "Long.: " << qGeoCoord.longitude() << endl
<< "Alt.: " << qGeoCoord.altitude() << endl;
}
});
So I just removed the QApplication part because I dont have this reference in a function. As a result i get:
Qt Version: 5.10.0
Try service: osm
Searching...
but no coordinates. I assume the connection to where the lat and lon data is acquired fails. But as I mentioned I dont understand the connect here.
Any help is appreciated
EDIT
so I tried to build a connect to catch the finished Signal as suggested.
The code now looks like this:
QGeoServiceProvider qGeoService("osm");
QGeoCodingManager *pQGeoCoder = qGeoService.geocodingManager();
if (!pQGeoCoder) {
cerr
<< "GeoCodingManager '" << "osm"
<< "' not available!" << endl;
}
QLocale qLocaleC(QLocale::C, QLocale::AnyCountry);
pQGeoCoder->setLocale(qLocaleC);
// build address
//QGeoAddress qGeoAddr;
qGeoAddr.setCountry(QString::fromUtf8("Germany"));
qGeoAddr.setPostalCode(QString::fromUtf8("88250"));
qGeoAddr.setCity(QString::fromUtf8("Weingarten"));
qGeoAddr.setStreet(QString::fromUtf8("Heinrich-Hertz-Str. 6"));
this->pQGeoCode = pQGeoCoder->geocode(qGeoAddr);
if (!pQGeoCode) {
cerr << "GeoCoding totally failed!" << endl;
}
cout << "Searching..." << endl;
connect(pQGeoCode,SIGNAL(finished()),this,SLOT(getlonlat()));
With the Slot:
void MainWindow::getlonlat()
{
QList<QGeoLocation> qGeoLocs = pQGeoCode->locations();
cout << qGeoLocs.size() << " location(s) returned." << endl;
for (QGeoLocation &qGeoLoc : qGeoLocs) {
qGeoLoc.setAddress(qGeoAddr);
QGeoCoordinate qGeoCoord = qGeoLoc.coordinate();
cout
<< "Lat.: " << qGeoCoord.latitude() << endl
<< "Long.: " << qGeoCoord.longitude() << endl
<< "Alt.: " << qGeoCoord.altitude() << endl;
}
}
However the finished signal doesn't get triggered. Therefore the result is the same.
EDIT
Implementing the Code from your Gui Answer:
MainWindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
// standard C++ header:
#include <iostream>
#include <sstream>
// Qt header:
#include <QGeoAddress>
#include <QGeoCodingManager>
#include <QGeoCoordinate>
#include <QGeoLocation>
#include <QGeoServiceProvider>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
QGeoServiceProvider *pQGeoProvider = nullptr;
ui->setupUi(this);
qDebug() << "Qt Version:" << QT_VERSION_STR;
// main application
//QApplication app(argc, argv);
// install signal handlers
QObject::connect(this->ui->qBtnInit, &QPushButton::clicked,
[&]() {
if (pQGeoProvider) delete pQGeoProvider;
std::ostringstream out;
pQGeoProvider = init(out);
log(out.str());
});
QObject::connect(this->ui->qBtnFind, &QPushButton::clicked,
[&]() {
// init geo coder if not yet done
if (!pQGeoProvider) {
std::ostringstream out;
pQGeoProvider = init(out);
log(out.str());
if (!pQGeoProvider) return; // failed
}
// fill in request
QGeoAddress *pQGeoAddr = new QGeoAddress;
pQGeoAddr->setCountry(this->ui->qTxtCountry->text());
pQGeoAddr->setPostalCode(this->ui->qTxtZipCode->text());
pQGeoAddr->setCity(this->ui->qTxtCity->text());
pQGeoAddr->setStreet(this->ui->qTxtStreet->text());
QGeoCodeReply *pQGeoCode
= pQGeoProvider->geocodingManager()->geocode(*pQGeoAddr);
if (!pQGeoCode) {
delete pQGeoAddr;
log("GeoCoding totally failed!\n");
return;
}
{ std::ostringstream out;
out << "Sending request for:\n"
<< pQGeoAddr->country().toUtf8().data() << "; "
<< pQGeoAddr->postalCode().toUtf8().data() << "; "
<< pQGeoAddr->city().toUtf8().data() << "; "
<< pQGeoAddr->street().toUtf8().data() << "...\n";
log(out.str());
}
// install signal handler to process result later
QObject::connect(pQGeoCode, &QGeoCodeReply::finished,
[&,pQGeoAddr, pQGeoCode]() {
// process reply
std::ostringstream out;
out << "Reply: " << pQGeoCode->errorString().toStdString() << '\n';
switch (pQGeoCode->error()) {
case QGeoCodeReply::NoError: {
// eval result
QList<QGeoLocation> qGeoLocs = pQGeoCode->locations();
out << qGeoLocs.size() << " location(s) returned.\n";
for (QGeoLocation &qGeoLoc : qGeoLocs) {
qGeoLoc.setAddress(*pQGeoAddr);
QGeoCoordinate qGeoCoord = qGeoLoc.coordinate();
out
<< "Lat.: " << qGeoCoord.latitude() << '\n'
<< "Long.: " << qGeoCoord.longitude() << '\n'
<< "Alt.: " << qGeoCoord.altitude() << '\n';
}
} break;
#define CASE(ERROR) \
case QGeoCodeReply::ERROR: out << #ERROR << '\n'; break
CASE(EngineNotSetError);
CASE(CommunicationError);
CASE(ParseError);
CASE(UnsupportedOptionError);
CASE(CombinationError);
CASE(UnknownError);
#undef CASE
default: out << "Undocumented error!\n";
}
// log result
log(out.str());
// clean-up
delete pQGeoAddr;
/* delete sender in signal handler could be lethal
* Hence, delete it later...
*/
pQGeoCode->deleteLater();
});
});
// fill in a sample request with a known address initially
this->ui->qTxtCountry->setText(QString::fromUtf8("Germany"));
this->ui->qTxtZipCode->setText(QString::fromUtf8("88250"));
this->ui->qTxtCity->setText(QString::fromUtf8("Weingarten"));
this->ui->qTxtStreet->setText(QString::fromUtf8("Danziger Str. 3"));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::log(const QString &qString)
{
this->ui->qTxtLog->setPlainText(this->ui->qTxtLog->toPlainText() + qString);
this->ui->qTxtLog->moveCursor(QTextCursor::End);
}
void MainWindow::log(const char *text)
{
log(QString::fromUtf8(text));
}
void MainWindow::log(const std::string &text)
{
log(text.c_str());
}
QGeoServiceProvider* MainWindow::init(std::ostream &out)
{
// check for available services
QStringList qGeoSrvList
= QGeoServiceProvider::availableServiceProviders();
for (QString entry : qGeoSrvList) {
out << "Try service: " << entry.toStdString() << '\n';
// choose provider
QGeoServiceProvider *pQGeoProvider = new QGeoServiceProvider(entry);
if (!pQGeoProvider) {
out
<< "ERROR: GeoServiceProvider '" << entry.toStdString()
<< "' not available!\n";
continue;
}
QGeoCodingManager *pQGeoCoder = pQGeoProvider->geocodingManager();
if (!pQGeoCoder) {
out
<< "ERROR: GeoCodingManager '" << entry.toStdString()
<< "' not available!\n";
delete pQGeoProvider;
continue;
}
QLocale qLocaleC(QLocale::C, QLocale::AnyCountry);
pQGeoCoder->setLocale(qLocaleC);
out << "Using service " << entry.toStdString() << '\n';
return pQGeoProvider; // success
}
out << "ERROR: No suitable GeoServiceProvider found!\n";
return nullptr; // all attempts failed
}
The main.cpp:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
And here the Header File for the MainWindow:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
// standard C++ header:
#include <iostream>
#include <sstream>
// Qt header:
#include <QGeoAddress>
#include <QGeoCodingManager>
#include <QGeoCoordinate>
#include <QGeoLocation>
#include <QGeoServiceProvider>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
virtual ~MainWindow();
void log(const QString &qString);
void log(const char *text);
void log(const std::string &text);
QGeoServiceProvider* init(std::ostream &out);
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
I transformed my older sample to a minimal application with GUI:
// standard C++ header:
#include <iostream>
#include <sstream>
// Qt header:
#include <QtWidgets>
#include <QGeoAddress>
#include <QGeoCodingManager>
#include <QGeoCoordinate>
#include <QGeoLocation>
#include <QGeoServiceProvider>
void log(QTextEdit &qTxtLog, const QString &qString)
{
qTxtLog.setPlainText(qTxtLog.toPlainText() + qString);
qTxtLog.moveCursor(QTextCursor::End);
}
void log(QTextEdit &qTxtLog, const char *text)
{
log(qTxtLog, QString::fromUtf8(text));
}
void log(QTextEdit &qTxtLog, const std::string &text)
{
log(qTxtLog, text.c_str());
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
// main application
QApplication app(argc, argv);
// setup GUI
QWidget qWin;
QVBoxLayout qBox;
QFormLayout qForm;
QLabel qLblCountry(QString::fromUtf8("Country:"));
QLineEdit qTxtCountry;
qForm.addRow(&qLblCountry, &qTxtCountry);
QLabel qLblZipCode(QString::fromUtf8("Postal Code:"));
QLineEdit qTxtZipCode;
qForm.addRow(&qLblZipCode, &qTxtZipCode);
QLabel qLblCity(QString::fromUtf8("City:"));
QLineEdit qTxtCity;
qForm.addRow(&qLblCity, &qTxtCity);
QLabel qLblStreet(QString::fromUtf8("Street:"));
QLineEdit qTxtStreet;
qForm.addRow(&qLblStreet, &qTxtStreet);
QLabel qLblProvider(QString::fromUtf8("Provider:"));
QComboBox qLstProviders;
qForm.addRow(&qLblProvider, &qLstProviders);
qBox.addLayout(&qForm);
QPushButton qBtnFind(QString::fromUtf8("Find Coordinates"));
qBox.addWidget(&qBtnFind);
QLabel qLblLog(QString::fromUtf8("Log:"));
qBox.addWidget(&qLblLog);
QTextEdit qTxtLog;
qTxtLog.setReadOnly(true);
qBox.addWidget(&qTxtLog);
qWin.setLayout(&qBox);
qWin.show();
// initialize Geo Service Providers
std::vector<QGeoServiceProvider*> pQGeoProviders;
{ std::ostringstream out;
QStringList qGeoSrvList
= QGeoServiceProvider::availableServiceProviders();
for (QString entry : qGeoSrvList) {
out << "Try service: " << entry.toStdString() << '\n';
// choose provider
QGeoServiceProvider *pQGeoProvider = new QGeoServiceProvider(entry);
if (!pQGeoProvider) {
out
<< "ERROR: GeoServiceProvider '" << entry.toStdString()
<< "' not available!\n";
continue;
}
QGeoCodingManager *pQGeoCoder = pQGeoProvider->geocodingManager();
if (!pQGeoCoder) {
out
<< "ERROR: GeoCodingManager '" << entry.toStdString()
<< "' not available!\n";
delete pQGeoProvider;
continue;
}
QLocale qLocaleC(QLocale::C, QLocale::AnyCountry);
pQGeoCoder->setLocale(qLocaleC);
qLstProviders.addItem(entry);
pQGeoProviders.push_back(pQGeoProvider);
out << "Service " << entry.toStdString() << " available.\n";
}
log(qTxtLog, out.str());
}
if (pQGeoProviders.empty()) qBtnFind.setEnabled(false);
// install signal handlers
QObject::connect(&qBtnFind, QPushButton::clicked,
[&]() {
// get current geo service provider
QGeoServiceProvider *pQGeoProvider
= pQGeoProviders[qLstProviders.currentIndex()];
// fill in request
QGeoAddress *pQGeoAddr = new QGeoAddress;
pQGeoAddr->setCountry(qTxtCountry.text());
pQGeoAddr->setPostalCode(qTxtZipCode.text());
pQGeoAddr->setCity(qTxtCity.text());
pQGeoAddr->setStreet(qTxtStreet.text());
QGeoCodeReply *pQGeoCode
= pQGeoProvider->geocodingManager()->geocode(*pQGeoAddr);
if (!pQGeoCode) {
delete pQGeoAddr;
log(qTxtLog, "GeoCoding totally failed!\n");
return;
}
{ std::ostringstream out;
out << "Sending request for:\n"
<< pQGeoAddr->country().toUtf8().data() << "; "
<< pQGeoAddr->postalCode().toUtf8().data() << "; "
<< pQGeoAddr->city().toUtf8().data() << "; "
<< pQGeoAddr->street().toUtf8().data() << "...\n";
log(qTxtLog, out.str());
}
// install signal handler to process result later
QObject::connect(pQGeoCode, &QGeoCodeReply::finished,
[&qTxtLog, pQGeoAddr, pQGeoCode]() {
// process reply
std::ostringstream out;
out << "Reply: " << pQGeoCode->errorString().toStdString() << '\n';
switch (pQGeoCode->error()) {
case QGeoCodeReply::NoError: {
// eval result
QList<QGeoLocation> qGeoLocs = pQGeoCode->locations();
out << qGeoLocs.size() << " location(s) returned.\n";
for (QGeoLocation &qGeoLoc : qGeoLocs) {
qGeoLoc.setAddress(*pQGeoAddr);
QGeoCoordinate qGeoCoord = qGeoLoc.coordinate();
out
<< "Lat.: " << qGeoCoord.latitude() << '\n'
<< "Long.: " << qGeoCoord.longitude() << '\n'
<< "Alt.: " << qGeoCoord.altitude() << '\n';
}
} break;
#define CASE(ERROR) \
case QGeoCodeReply::ERROR: out << #ERROR << '\n'; break
CASE(EngineNotSetError);
CASE(CommunicationError);
CASE(ParseError);
CASE(UnsupportedOptionError);
CASE(CombinationError);
CASE(UnknownError);
#undef CASE
default: out << "Undocumented error!\n";
}
// log result
log(qTxtLog, out.str());
// clean-up
delete pQGeoAddr;
/* delete sender in signal handler could be lethal
* Hence, delete it later...
*/
pQGeoCode->deleteLater();
});
});
// fill in a sample request with a known address initially
qTxtCountry.setText(QString::fromUtf8("Germany"));
qTxtZipCode.setText(QString::fromUtf8("88250"));
qTxtCity.setText(QString::fromUtf8("Weingarten"));
qTxtStreet.setText(QString::fromUtf8("Danziger Str. 3"));
// runtime loop
app.exec();
// done
return 0;
}
Compiled and tested in cygwin on Windows 10 (64 bit):
$ g++ --version
g++ (GCC) 6.4.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ qmake-qt5 testQGeoAddressGUI.pro
$ make
g++ -c -fno-keep-inline-dllexport -D_GNU_SOURCE -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_LOCATION_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_POSITIONING_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -I. -isystem /usr/include/qt5 -isystem /usr/include/qt5/QtWidgets -isystem /usr/include/qt5/QtLocation -isystem /usr/include/qt5/QtQuick -isystem /usr/include/qt5/QtGui -isystem /usr/include/qt5/QtPositioning -isystem /usr/include/qt5/QtQml -isystem /usr/include/qt5/QtNetwork -isystem /usr/include/qt5/QtCore -I. -I/usr/lib/qt5/mkspecs/cygwin-g++ -o testQGeoAddressGUI.o testQGeoAddressGUI.cc
g++ -o testQGeoAddressGUI.exe testQGeoAddressGUI.o -lQt5Widgets -lQt5Location -lQt5Quick -lQt5Gui -lQt5Positioning -lQt5Qml -lQt5Network -lQt5Core -lGL -lpthread
$ ./testQGeoAddressGUI
Qt Version: 5.9.2
Notes:
When I wrote this sample I was very carefully about scope and life-time of involved variables. (Actually, I changed some local variables to pointers and instances created with new to achieve this.) This is my hint for any reader.
In my 1st test, the application ended up in CommunicationError. I'm quite sure that our company's security policy is responsible for this. (I did the same with my older sample which I tested successfully at home – with the same result.)
A 2nd test (at home) went better. First, I tried to find the address with service provider osm which brought 0 results. Changing the service provider to esri returned one result.
I copied the output to maps.google.de:
This is actually the correct result – as I tested the (new) address of the company EKS InTec where I'm working.
The usage of lambdas in the above sample makes it a bit hard to read. Therefore, I re-visited the sample. Now, all relevant stuff has moved to a class MainWindow (hopefully, closer to the requirement of OP). The lambdas were replaced by simple methods.
// standard C++ header:
#include <iostream>
#include <sstream>
// Qt header:
#include <QtWidgets>
#include <QGeoAddress>
#include <QGeoCodingManager>
#include <QGeoCoordinate>
#include <QGeoLocation>
#include <QGeoServiceProvider>
// main window class
class MainWindow: public QWidget {
// variables:
private:
// list of service providers
std::vector<QGeoServiceProvider*> pQGeoProviders;
// Qt widgets (contents of main window)
QVBoxLayout qBox;
QFormLayout qForm;
QLabel qLblCountry;
QLineEdit qTxtCountry;
QLabel qLblZipCode;
QLineEdit qTxtZipCode;
QLabel qLblCity;
QLineEdit qTxtCity;
QLabel qLblStreet;
QLineEdit qTxtStreet;
QLabel qLblProvider;
QComboBox qLstProviders;
QPushButton qBtnFind;
QLabel qLblLog;
QTextEdit qTxtLog;
// methods:
public: // ctor/dtor
MainWindow(QWidget *pQParent = nullptr);
virtual ~MainWindow();
MainWindow(const MainWindow&) = delete;
MainWindow& operator=(const MainWindow&) = delete;
private: // internal stuff
void init(); // initializes geo service providers
void find(); // sends request
void report(); // processes reply
void log(const QString &qString)
{
qTxtLog.setPlainText(qTxtLog.toPlainText() + qString);
qTxtLog.moveCursor(QTextCursor::End);
}
void log(const char *text) { log(QString::fromUtf8(text)); }
void log(const std::string &text) { log(text.c_str()); }
};
MainWindow::MainWindow(QWidget *pQParent):
QWidget(pQParent),
qLblCountry(QString::fromUtf8("Country:")),
qLblZipCode(QString::fromUtf8("Postal Code:")),
qLblCity(QString::fromUtf8("City:")),
qLblStreet(QString::fromUtf8("Street:")),
qLblProvider(QString::fromUtf8("Provider:")),
qBtnFind(QString::fromUtf8("Find Coordinates")),
qLblLog(QString::fromUtf8("Log:"))
{
// setup child widgets
qForm.addRow(&qLblCountry, &qTxtCountry);
qForm.addRow(&qLblZipCode, &qTxtZipCode);
qForm.addRow(&qLblCity, &qTxtCity);
qForm.addRow(&qLblStreet, &qTxtStreet);
qForm.addRow(&qLblProvider, &qLstProviders);
qBox.addLayout(&qForm);
qBox.addWidget(&qBtnFind);
qBox.addWidget(&qLblLog);
qBox.addWidget(&qTxtLog);
setLayout(&qBox);
// init service provider list
init();
// install signal handlers
QObject::connect(&qBtnFind, &QPushButton::clicked,
this, &MainWindow::find);
// fill in a sample request with a known address initially
qTxtCountry.setText(QString::fromUtf8("Germany"));
qTxtZipCode.setText(QString::fromUtf8("88250"));
qTxtCity.setText(QString::fromUtf8("Weingarten"));
qTxtStreet.setText(QString::fromUtf8("Danziger Str. 3"));
}
MainWindow::~MainWindow()
{
// clean-up
for (QGeoServiceProvider *pQGeoProvider : pQGeoProviders) {
delete pQGeoProvider;
}
}
void MainWindow::init()
{
// initialize Geo Service Providers
{ std::ostringstream out;
QStringList qGeoSrvList
= QGeoServiceProvider::availableServiceProviders();
for (QString entry : qGeoSrvList) {
out << "Try service: " << entry.toStdString() << '\n';
// choose provider
QGeoServiceProvider *pQGeoProvider = new QGeoServiceProvider(entry);
if (!pQGeoProvider) {
out
<< "ERROR: GeoServiceProvider '" << entry.toStdString()
<< "' not available!\n";
continue;
}
QGeoCodingManager *pQGeoCoder = pQGeoProvider->geocodingManager();
if (!pQGeoCoder) {
out
<< "ERROR: GeoCodingManager '" << entry.toStdString()
<< "' not available!\n";
delete pQGeoProvider;
continue;
}
QLocale qLocaleC(QLocale::C, QLocale::AnyCountry);
pQGeoCoder->setLocale(qLocaleC);
qLstProviders.addItem(entry);
pQGeoProviders.push_back(pQGeoProvider);
out << "Service " << entry.toStdString() << " available.\n";
}
log(out.str());
}
if (pQGeoProviders.empty()) qBtnFind.setEnabled(false);
}
std::string format(const QGeoAddress &qGeoAddr)
{
std::ostringstream out;
out
<< qGeoAddr.country().toUtf8().data() << "; "
<< qGeoAddr.postalCode().toUtf8().data() << "; "
<< qGeoAddr.city().toUtf8().data() << "; "
<< qGeoAddr.street().toUtf8().data();
return out.str();
}
void MainWindow::find()
{
// get current geo service provider
QGeoServiceProvider *pQGeoProvider
= pQGeoProviders[qLstProviders.currentIndex()];
// fill in request
QGeoAddress qGeoAddr;
qGeoAddr.setCountry(qTxtCountry.text());
qGeoAddr.setPostalCode(qTxtZipCode.text());
qGeoAddr.setCity(qTxtCity.text());
qGeoAddr.setStreet(qTxtStreet.text());
QGeoCodeReply *pQGeoCode
= pQGeoProvider->geocodingManager()->geocode(qGeoAddr);
if (!pQGeoCode) {
log("GeoCoding totally failed!\n");
return;
}
{ std::ostringstream out;
out << "Sending request for:\n"
<< format(qGeoAddr) << "...\n";
log(out.str());
}
// install signal handler to process result later
QObject::connect(pQGeoCode, &QGeoCodeReply::finished,
this, &MainWindow::report);
/* This signal handler will delete it's own sender.
* Hence, the connection need not to be remembered
* although it has only a limited life-time.
*/
}
void MainWindow::report()
{
QGeoCodeReply *pQGeoCode
= dynamic_cast<QGeoCodeReply*>(sender());
// process reply
std::ostringstream out;
out << "Reply: " << pQGeoCode->errorString().toStdString() << '\n';
switch (pQGeoCode->error()) {
case QGeoCodeReply::NoError: {
// eval result
QList<QGeoLocation> qGeoLocs = pQGeoCode->locations();
out << qGeoLocs.size() << " location(s) returned.\n";
for (QGeoLocation &qGeoLoc : qGeoLocs) {
QGeoAddress qGeoAddr = qGeoLoc.address();
QGeoCoordinate qGeoCoord = qGeoLoc.coordinate();
out
<< "Coordinates for "
<< qGeoAddr.text().toUtf8().data() << ":\n"
<< "Lat.: " << qGeoCoord.latitude() << '\n'
<< "Long.: " << qGeoCoord.longitude() << '\n'
<< "Alt.: " << qGeoCoord.altitude() << '\n';
}
} break;
#define CASE(ERROR) \
case QGeoCodeReply::ERROR: out << #ERROR << '\n'; break
CASE(EngineNotSetError);
CASE(CommunicationError);
CASE(ParseError);
CASE(UnsupportedOptionError);
CASE(CombinationError);
CASE(UnknownError);
#undef CASE
default: out << "Undocumented error!\n";
}
// log result
log(out.str());
// clean-up
/* delete sender in signal handler could be lethal
* Hence, delete it later...
*/
pQGeoCode->deleteLater();
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
// main application
QApplication app(argc, argv);
// setup GUI
MainWindow win;
win.show();
// runtime loop
app.exec();
// done
return 0;
}
Note:
The look and behavior is the same like for the above example. I changed the output of reply a bit.
While preparing this sample, I realized that it is actually not necessary to set the address of the returned QGeoLocation as it is already there. IMHO, it is interesting that the returned address looks a bit different than the requested. It seems that it is returned in a (I would say) normalized form.

Modify "Modbus TCP" to "Modbus RTU" with QT - QModbusTcpClient

I would have to send Modbus request for specific data, my problem is that I have to use mobile communication, wifi, connect to a custom electronic card, which is right in Modbus RTU.
My working code connecting to the electronic board:
#include "connessione.h"
#include <QModbusTcpClient>
#include <QVariant>
#include <QModbusDataUnit>
#include <QDebug>
connessione::connessione(QObject *parent) : QObject(parent)
{
qDebug() << "here debug " << "ok";
clientX = new QModbusTcpClient();
clientX->setConnectionParameter(QModbusDevice::NetworkAddressParameter, "192.168.222.1");
clientX->setConnectionParameter(QModbusDevice::NetworkPortParameter, 5555);
if (clientX->connectDevice())
{
qDebug() << "connected: " << clientX->state();
}
else
{
qDebug() << "ERRORE" << clientX->errorString();
}
}
void connessione::clickButton(){
QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters, 0, 1); // just read input register 40006
//qDebug() << "readUnit" << readUnit.RegisterType;
qDebug() << "readUnit" << clientX->state();
if (auto *reply = clientX->sendReadRequest(readUnit, 255)) // client id 255
{
if (!reply->isFinished())
{
// connect the finished signal of the request to your read slot
qDebug() << "connected" << reply->errorString();
connect(reply, &QModbusReply::finished, this, &connessione::readReady);
}
else
{
qDebug() << "Errore" << reply->errorString();
delete reply; // broadcast replies return immediately
}
}
else
{
qDebug() << "Errore" << reply->errorString();
// request error
}
}
void connessione::readReady()
{
QModbusReply *reply = qobject_cast<QModbusReply *>(sender());
if (!reply)
return;
if (reply->error() == QModbusDevice::NoError)
{
const QModbusDataUnit unit = reply->result();
int startAddress = unit.startAddress(); // the start address,
int value = unit.value(0); // value of the start address + 0
qDebug() << "NESSUN ERRORE" << reply->errorString();
}
else
{
qDebug() << "Errore readReady" << reply->errorString();
// reply error
}
reply->deleteLater(); // delete the reply
}
log string TCP sent:
D/libmodbusMobile.so( 8042): (null):0 ((null)): qt.modbus: (TCP
client) Sent TCP PDU: 0x0300000001 with tId: 2
this is right: 0x0300000001
But unfortunately, my electronic card, the integrated firmware I can not modify, is right with Modbus RTU, so I should change 0x0300000001 to 0x010300000001C1C2 where C1 and C2 are the checksums.
I believe that QModbusDataUnit generate buffer to send. So how to change it? Exist manual solution where I build the buffer?
how to change it and create custom send buffer like the example?
Thanks

QSerialPort::readLine doesn't work as expected on MS Windows

I'm trying to connect a micro-controller with my desktop PC via USB-serial cable.
The OS of my desktop PC is Windows 8.1, and USB-serial cable is TTL-232R-3V3. (FTDI)
(Qt version: 5.2.0 beta1, QtCreator Version: 3.0, Compiler: MSVC2012)
Now I'm trying read/write loop-back tests, and that's why RX/TX pin of USB-serial cable are connected with each other.
Here is my code.
#include <QtCore/QCoreApplication>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QtCore/QDebug>
#define PORT_NAME "COM3"
#define BAUDRATE 19600
#define TIMEOUT_MS 1000
QT_USE_NAMESPACE
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QSerialPort pSerial(PORT_NAME);
const char strMsg[] = "#1:Send data line \n #2:Send data line\n #3:Send data line end\n";
char strBuf[256];
qint64 nByte;
if(pSerial.open(QIODevice::ReadWrite)){
pSerial.setBaudRate(BAUDRATE);
qDebug() << "OPEN PASS";
pSerial.write(strMsg);
pSerial.flush();
if(pSerial.waitForBytesWritten(TIMEOUT_MS)){
qDebug() << "WRITE PASS";
}
pSerial.waitForReadyRead(TIMEOUT_MS);
while(true){
if( pSerial.canReadLine()){
qDebug() << "CAN READ LINE";
nByte = pSerial.readLine(strBuf,sizeof(strBuf));
qDebug() << "Length: " << nByte;
qDebug() << "Read data: " << strBuf;
}
}
pSerial.close();
} else {
qDebug() << "OPEN FAIL\n";
}
return a.exec();
}
When the program starts to run, the result is different than I expected.
Only first line of sent data can be received. So, "Read data: #1 Send data line" is printed
on console. But the rest of sent data will never be received. Does anyone know why?
Any help would be appreciated.
Thanks in advance.
EDIT: I revised my code according to Papp's comment.Then it works as I expected.
All sent message has been received.
Does it mean I misunderstand the usage about readLine() or canReadLine()?
// while(true){
// if( pSerial.canReadLine()){
// qDebug() << "CAN READ LINE";
// nByte = pSerial.readLine(strBuf,sizeof(strBuf));
// qDebug() << "Length: " << nByte;
// qDebug() << "Read data: " << strBuf;
// }
// }
pSerial.waitForReadyRead(TIMEOUT_MS);
QByteArray readData = pSerial.readAll();
while (pSerial.waitForReadyRead(TIMEOUT_MS)) {
readData.append(pSerial.readAll());
}
qDebug() << "Read data: " << readData;
EDIT 2nd time : Following code also works for me.
while(true){
if( pSerial.waitForReadyRead(TIMEOUT_MS) && pSerial.canReadLine()){ // I revised this line
qDebug() << "CAN READ LINE";
nByte = pSerial.readLine(strBuf,sizeof(strBuf));
qDebug() << "Length: " << nByte;
qDebug() << "Read data: " << strBuf;
qDebug() << "Error Message: " << pSerial.errorString();
}
}
That is because you need to read in a loop like this:
QByteArray readData = serialPort.readAll();
while (serialPort.waitForReadyRead(5000))
readData.append(serialPort.readAll());
Please see the creadersync example for the details what I added to 5.2. You can also check the creaderasync example for non-blocking operation.
To be fair, we have not tested readLine that much, but it works for me on Unix, so does it on Windows for someone else.
The mistake that you've made is expecting to receive all the sent data when waitForReadyRead returns. When waitForReadyRead finishes, all you're guaranteed is some data being available to be read. It may be as little as one character, not necessarily a whole line.
The loop from your last modification is the almost correct way to do it. You should nest reading of the lines in a separate loop. The following code is how it should be done, and agrees with the semantics of QIODevice:
while (pSerial.waitForReadyRead(TIMEOUT_MS)) {
while (pSerial.canReadLine()) {
qDebug() << "NEW LINE";
QByteArray line = pSerial.readLine();
qDebug() << "Length: " << line.size();
qDebug() << "Read data: " << line;
qDebug() << "Error Message: " << pSerial.errorString();
}
}
qDebug << "TIMED OUT";
Note that none of this code should even run in the GUI thread. Ideally you should move it to a QObject, use the signals emitted by QIODevice (and thus QSerialPort), and move that object to a separate thread.
The GUI thread can sometimes block for long periods of time, it's not normally desirable to have it disturb the timeliness of your device communication. Similarly, you don't want device timeouts to block the GUI thread. Both are equally bad and are a very common source of bad user experience. Qt makes multithreading very easy - leverage it for your user's sake, and do it properly.
On Linux I have to do it this way to receive ASCII text ending with '\n'
QByteArray readData = pSerial.readAll();
while (readData[readData.length() - 1] != '\n') {
pSerial.waitForReadyRead(5000);
readData.append(pSerial.readAll());
}
QString result(readData);
QSerialPort::readLine() doesn't work for me either