Why is QMediaPlayer::duration() giving me a value of -1? - c++

Here is my code:
QString BoatProgramming::setDuration(QString path)
{
if (path.isNull()) { return ""; }
QMediaPlayer mp;
mp.setMedia(QUrl::fromLocalFile("/home/akiva/deleteme.ogg"));
qDebug() << mp.duration(); // Outputting a value of -1
m_Duration = QString::number(mp.duration());
emit durationChanged();
return m_Duration;
}
There is obviously an error somewhere, but beyond checking the filename, I am lamentably amiss as to what the problem is. Could it be simply that .ogg is not supported? Am I calling the function before the object is fully loaded into memory? Or is it something else?
Thanks.

QMediaPlayer::setMedia() performs loading asynchronously, from the docs:
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 when an error occurs during loading.
This means that querying QMediaPlayer for duration() after calling setMedia() immediately may not work, since QMediaPlayer might not have loaded the media yet.
In order to guarantee that loading has finished before calling duration(), you have to listen for mediaStatusChanged() signal, and get duration() only when mediaStatus() returns QMediaPlayer::LoadedMedia. Here is a minimal example:
#include <QtWidgets>
#include <QtMultimedia>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
QLabel label; //a label to display duration
QString fileName = QFileDialog::getOpenFileName(nullptr, "Open Media File");
QMediaPlayer mp;
mp.setMedia(QUrl::fromLocalFile(fileName));
QObject::connect(&mp, &QMediaPlayer::mediaStatusChanged,
[&](QMediaPlayer::MediaStatus status){
if(status == QMediaPlayer::LoadedMedia) //when loading is finished
{
//show duration in a label
qint64 duration= mp.duration();
label.setText(QString("Duration: %1 ms.\n\nThat is: %2")
.arg(duration)
.arg(QDateTime::fromTime_t(duration/1000).toUTC()
.toString("hh:mm:ss")));
label.show();
}
});
return app.exec();
}

Related

The Logs are not showing in QT Application Window

i am making a Tail Log application. It is executing perfectly but the output window is not showing any results.
Below is the code:
LogTail.cpp
#include "logtail.h"
#include <QApplication>
#include <QTextEdit>
#include<QDebug>
//start id=constructor
LogTail::LogTail(QString fn) {
//if (fn == QString()) {
// fn = "C:/Users/arpit.k/OneDrive - Accord Software & Systems Pvt. Ltd/Documents/QT/LogTail3/sample.log";
// }
connect (this, SIGNAL(readyReadStandardOutput()),
this, SLOT(logOutput())); /* When there is input ready, call this slot.*/
QStringList argv;
argv << "-f" << fn; /* tail -f filename */
start("tail", argv); /* Returns immediately, and now there is a child process running, "attached"
to this process. When this process exits, the child tail process will also terminate. */
}
LogTail::~LogTail() {
terminate(); /* Attempts to terminate this process. */
}
//end
//start id=logOutput
// tail sends its output to stdout.
void LogTail::logOutput() { /* Slot called whenever there is input to read. */
QByteArray bytes = readAllStandardOutput();
QStringList lines = QString(bytes).split("\n");
foreach (QString line, lines) {
//qDebug() << line;
emit logString(line);
}
}
//end
main.cpp
#include "logtail.h"
#include <QTextEdit>
#include <QApplication>
int main (int argc, char* argv[]) {
QApplication app(argc, argv);
QStringList al = app.arguments();
QTextEdit textEdit;
textEdit.setWindowTitle("Debug");
textEdit.setWindowTitle("logtail demo");
QString filename = "sample.log";
if (al.size() > 1) filename = al[1];
LogTail tail(filename); /* Create object, starts process too. */
tail.connect (&tail, SIGNAL(logString(const QString&)),
&textEdit, SLOT(append(const QString&)));
textEdit.show();
return app.exec();
}
Note: sample.log file contains some log data
Output window doesn't show any logs:
(https://i.stack.imgur.com/ENhqs.png)
I need it to display recent 10 Log lines!
Assuming you are subclassing from QProcess (you should post a complete minimal example in order to get an accurate answer).
You are starting the process in the constructor of LogTail, 'tail' will probably output some data or some events that will trigger readyReadStandardOutput before you have the chance to connect the signal in the main function.
readyReadStandardOutput will be triggered only once as you never cleared the input buffer (you never had the chance to read the available data).
So in order to make your example work you should call logOutput after you start the process.
Anyway I will suggest you to implement differently, I would call start from outside or at least outside the constructor, that way your object will be more scalable.

Multiple get/post calls using a wrapper class with Qt

I am working on a Qt project with a team. I have two functions — one retrives the numerical coordinates of a place, the other downloads the map of the place — that I want to merge in one wrapper class, so that my teammates can call it easily.
#include <QCoreApplication>
#include <QFile>
#include <QHttpMultiPart>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <iostream>
class OpenStreetMapWrapper: public QObject{
Q_OBJECT
public:
OpenStreetMapWrapper(QObject *parent=nullptr):QObject(parent){
connect(&manager, &QNetworkAccessManager::finished, this, &OpenStreetMapWrapper::handle_finished);
}
void download(const std::string &region, const std::string &department, const QFile& outfile){
QNetworkRequest request;
QUrl url = QUrl(QString::fromStdString("https://download.openstreetmap.fr/extracts/europe/france/" + region + "/" + department + ".osm.pbf"));
request.setUrl(url);
request.setAttribute(QNetworkRequest::User, outfile.fileName());
manager.get(request);
}
void searchCSV(QFile& file, QFile& outfile){
QNetworkRequest request(QUrl("https://api-adresse.data.gouv.fr/search/csv/")); // Free API provided by the French government
request.setAttribute(QNetworkRequest::User, outfile.fileName());
QHttpMultiPart *multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart postpart;
postpart.setHeader(QNetworkRequest::ContentDispositionHeader,
QString("form-data; name=%1; filename=%2")
.arg("data", file.fileName()));
postpart.setBodyDevice(&file);
multipart->append(postpart);
file.setParent(multipart);
manager.post(request, multipart);
}
private:
QNetworkAccessManager manager;
void handle_finished(QNetworkReply *reply){
if(reply->error() == QNetworkReply::NoError){
QByteArray read = reply->readAll();
std::cout << read.toStdString() << std::endl; // For debugging
QString filename = reply->request().attribute(QNetworkRequest::User).toString();
QFile out(filename);
if(out.open(QIODevice::WriteOnly)){
out.write(read);
out.close();
}
}
else{
qDebug() << reply->error() << reply->errorString();
}
reply->deleteLater();
// QCoreApplication::quit(); This is done somewhere else?
}
};
#include <main.moc>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
OpenStreetMapWrapper A;
QFile file("./search.csv");
file.open(QIODevice::ReadWrite);
QFile outfile("./output.csv");
outfile.open(QIODevice::ReadWrite);
// Search
A.searchCSV(file, outfile); // 1st call works
A.searchCSV(file, outfile); // 2nd call -> makes both calls fail.
// Downloader
std::string region = "corse";
std::string department = "haute_corse";
return a.exec();
}
The problem with the code above, is that when for example searchCSV is called it displays the output as needed, but if it is called twice in the code, there is no output at all. After some debugging I think the problem is that the manager and handle_finished are not connected properly, because the execution never reaches there. Is there a simple way to solve this issue? Ideally, there is just one class instant, and any method can be called any number of times.
I don't know much about Qt, but it looks like you're trying to read from file twice and my guess is that when it reaches the end of the file after the first call to A.searchCSV it is done and you can't read from it anymore - unless you reposition the QFile to the beginning of the file.
Possible solution:
A.searchCSV(file, outfile);
file.unsetError(); // possibly needed
file.seek(0); // rewind to the start of the file
A.searchCSV(file, outfile);
The two QFile (input, output) are shared between two asynchronous calls (searchCSV) that might give an undefined behavior. The input file (stream) contents will be load and push only after the connection was made (like curl does).
You should:
Make searchCSV a blocking function (wait until handle_finished() done), input file pointer should be reinitialized before an other call.
OR: Use separated input/output QFile instances

Working with QFutures and OpenCV in Qt/C++

I have a simple Qt/C++ program which gathers a webcam image out of one of my LAN devices using a cv::VideoCapture object.
The application is being built using Qt Quick and has an Image QML Item which is being served with a picture every 500 milliseconds via a custom QQuickImageProvider implementation:
WebcamImageProvider::WebcamImageProvider()
: QQuickImageProvider(ImageType::Image)
{
connect(&_timer, &QTimer::timeout, this, &WebcamImageProvider::updateImage);
// refresh our picture every .5 seconds
_timer.start(500);
}
// overridden function from base class
QImage WebcamImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
Q_UNUSED(id)
Q_UNUSED(size)
Q_UNUSED(requestedSize)
return _img;
}
My main function looks like this:
// Qt Quick
#include <QQuickItem>
#include <QQuickWindow>
// QML + GUI
#include <QQmlContext>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
// webcam stuff
#include "webcamimageprovider.h"
/*
* Here we go
*/
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
// we're adding our self-made image provider instance here
WebcamImageProvider wip;
engine.addImageProvider("webcam", &wip);
engine.rootContext()->setContextProperty("webcamImageProvider", &wip);
engine.load(url);
return app.exec();
The code of my main.qml file should not be important here.
Inside my image provider class, I have provided the updateImage slot as follows:
void WebcamImageProvider::updateImage()
{
/*
* Webcam image grapping via https://raspberrypi.stackexchange.com/a/85510/132942
*/
if (!_cap.open(_videoUrl)) {
qDebug() << "Error opening stream.";
}
if (!_cap.read(_mat)) {
qDebug() << "No video capture or webcam error.";
}
// via https://stackoverflow.com/a/12312326/4217759
_img = QImage(
static_cast<uchar*>(_mat.data),
_mat.cols,
_mat.rows,
static_cast<int>(_mat.step),
QImage::Format_BGR888
);
emit imageChanged(_img);
}
My problem here is that when my device is not reachable via network, the application will freeze completely as it gets stuck in the _cap.open() function. Therefore, I am trying to outsource this function to a future so the image will be loaded asynchronously. Since I have literally zero idea of threading and futures, I randomly gave it a shot with using QFutures:
void WebcamImageProvider::updateImage()
{
/*
* Webcam image grapping via https://raspberrypi.stackexchange.com/a/85510/132942
*/
QFuture<void> future = QtConcurrent::run([&](){ _cap.open(_videoUrl); });
if (!future.isRunning()) {
qDebug() << "Error opening stream.";
}
if (future.isFinished()) {
if (!_cap.read(_mat)) {
qDebug() << "No video capture or webcam error.";
}
// via https://stackoverflow.com/a/12312326/4217759
_img = QImage(
static_cast<uchar*>(_mat.data),
_mat.cols,
_mat.rows,
static_cast<int>(_mat.step),
QImage::Format_BGR888
);
emit imageChanged(_img);
}
}
However, I won't get any image output from that.
Can someone help me on how to structure the code correctly so I can load my image asynchronously here?
Thanks a lot in advance.
1- you should know something , create setter for _img and emit changed in that ! because it is possible one day you need set Image in another functions and if you don't do this , you should duplicate emit fooChanged().
2- when you check these and is's not responsible for working in your method , you should throw an exception then handle them and if you wanted use qDebug().
3- my suggest is that (if I understood completely your job) create an thread that working in loop and always getting new images then create a queue worker and create another thread (worker) for process your scenario(here updateImage method).

How to auto restart a Qt application when it crashes, within the same program?

Is there any relatively "standard" design to auto restart a Qt application program, when it crashes abnormally?
Specific to Windows, do I have to use any windows service?
Or if I have to write another program separately, then how to do that?
Here's how you might do it using a single application that can act either as a monitor or as business logic. It's akin to Jon Harper's answer, except in code, not prose :)
Of Note
The monitor should not instantiate a QApplication nor QGuiApplication: it has no UI. Otherwise, redundant running process indicators will appear on some platforms (i.e. OS X, Win 10).
The monitor/business logic selection is achieved via setting an environment variable in the called process.
Passing the monitor/business logic selection via command line arguments is problematic, as the command line switch would need to be filtered out -- doing that portably without running into corner cases is tricky.
The monitor process forwards the console I/O of the business logic process, as well as the return code.
// https://github.com/KubaO/stackoverflown/tree/master/questions/appmonitor-37524491
#include <QtWidgets>
#include <cstdlib>
#if defined(Q_OS_WIN32)
#include <windows.h>
#else
static void DebugBreak() { abort(); }
#endif
static int businessLogicMain(int &argc, char **argv) {
QApplication app{argc, argv};
qDebug() << __FUNCTION__ << app.arguments();
QWidget w;
QHBoxLayout layout{&w};
QPushButton crash{"Crash"}; // purposefully crash for testing
QPushButton quit{"Quit"}; // graceful exit, which doesn't need restart
layout.addWidget(&crash);
layout.addWidget(&quit);
w.show();
QObject::connect(&crash, &QPushButton::clicked, DebugBreak);
QObject::connect(&quit, &QPushButton::clicked, &QCoreApplication::quit);
return app.exec();
}
static char const kRunLogic[] = "run__business__logic";
static char const kRunLogicValue[] = "run__business__logic";
#if defined(Q_OS_WIN32)
static QString getWindowsCommandLineArguments() {
const wchar_t *args = GetCommandLine();
bool oddBackslash = false, quoted = false, whitespace = false;
// skip the executable name according to Windows command line parsing rules
while (auto c = *args) {
if (c == L'\\')
oddBackslash ^= 1;
else if (c == L'"')
quoted ^= !oddBackslash;
else if (c == L' ' || c == L'\t')
whitespace = !quoted;
else if (whitespace)
break;
else
oddBackslash = false;
args++;
}
return QString::fromRawData(reinterpret_cast<const QChar*>(args), lstrlen(args));
}
#endif
static int monitorMain(int &argc, char **argv) {
#if !defined(Q_OS_WIN32)
QStringList args;
args.reserve(argc-1);
for (int i = 1; i < argc; ++i)
args << QString::fromLocal8Bit(argv[i]);
#endif
QCoreApplication app{argc, argv};
QProcess proc;
auto onFinished = [&](int retcode, QProcess::ExitStatus status) {
qDebug() << status;
if (status == QProcess::CrashExit)
proc.start(); // restart the app if the app crashed
else
app.exit(retcode); // no restart required
};
QObject::connect(&proc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), onFinished);
auto env = QProcessEnvironment::systemEnvironment();
env.insert(kRunLogic, kRunLogicValue);
proc.setProgram(app.applicationFilePath()); // logic and monitor are the same executable
#if defined(Q_OS_WIN32)
SetErrorMode(SEM_NOGPFAULTERRORBOX); // disable Windows error reporting
proc.setNativeArguments(getWindowsCommandLineArguments()); // pass command line arguments natively
env.insert("QT_LOGGING_TO_CONSOLE", "1"); // ensure that the debug output gets passed along
#else
proc.setArguments(args);
#endif
proc.setProcessEnvironment(env);
proc.setProcessChannelMode(QProcess::ForwardedChannels);
proc.start();
return app.exec();
}
int main(int argc, char **argv) {
if (qgetenv(kRunLogic) != kRunLogicValue)
return monitorMain(argc, argv);
else
return qunsetenv(kRunLogic), businessLogicMain(argc, argv);
}
If an application crashes, it's done.
Your monitor idea is a good one, and can be achieved using QProcess. Use the "monitor" to bootstrap your actual application. To do this, implement a monitoring object with a QProcess member. In pseudocode:
class MonitorObject : public QObject
{
...
public Q_SLOTS:
void onStarted();
void onFinished(int, QProcess::ExitStatus);
...
private:
QProcess m_process;
}
Then in main:
Create a QCoreApplication and a monitoring object on the stack.
Send a queued signal to your monitor object so it knows when the main event loop starts. You can achieve this using QMetaObject::invoke with a Qt::QueuedConnection:
int main(...)
{
QCoreApplication app;
MonitorObject monitor;
... // other initialization code here
QMetaObject::invoke(&monitor, "onStarted", Qt::QueuedConnection);
return app.exec();
}
And in your MonitorObject:
Connect QProcess's finished signal to onFinished.
When MonitorObject::onStarted is called, start the process.
When the QProcess::finished signal fires, either restart the offending program or exit, depending on the exitCode argument in the emitted signal.
I don't know of any standard Qt method for restarting apps when they crash. But there is a nice class available which makes writing a supervisor/monitor class very easy. It is called QProcess.
You can start the process like this:
monitorClass::startProcess(QString commandLine) // e.g. "c:\mytestapp.exe param1 param2"
{
mp_Process = new QProcess(this);
mp_Process->start(commandLine);
mp_Process->waitForStarted();
// Start a timer
mp_Timer->start(1000);
}
Then when the timer expires (every second - or whatever)
void monitorClass::TimerExpired(void)
{
switch (mp_Process->state())
{
default:
case QProcess::NotRunning:
{
qDebug("Process has stopped un-expectedly\n");
// Tell the supervisor that the process has terminated
// restart the process
startProcess("c:\mytestapp.exe param1 param2"); // just an example
break;
}
case QProcess::Starting:
case QProcess::Running:
{
qDebug("Process is running ok\n");
break;
}
}
}
Note
This is really pseudo code, its not a compilable example - it is just to show you roughly the how easy it is to do this with QProcess...

maximizing the already running instance of a single instance app in qt

I have made an application to run only single instance using shared memory in qt.My code looks like this
int main(int argc, char *argv[])
{
QSharedMemory sharedMemory;
sharedMemory.setKey("4da7b8a28a378e7fa9004e7a95cf257f");
if(!sharedMemory.create(1))
{
return 1; // Exit already a process running
}
QApplication a(argc, argv);
Encoder *encoder = Encoder::instance();
encoder->show();
return a.exec();
}
Now, I need to show the already running instance to the user (Maximize the window), when they try to run another instance. How can I achieve this?
There is an easy setup using QtSingleApplication instead :
QtSingleApplication app("myapp",argc, argv);
if (app.isRunning()) {
QListIterator<QString> it(messagestosend);
QString rep("Another instance is running, so I will exit.");
bool sentok = false;
while(it.hasNext()){
sentok = app.sendMessage(it.next(),1000);
if(!sentok)
break;
}
rep += sentok ? " Message sent ok." : " Message sending failed; the other instance may be frozen.";
return 0;
}
To receive this message you should listen with your desired slot to the signal
void QtSingleApplication::messageReceived(const QString&)