So I have an interesting problem....a program I am (trying) to write is crashing with this error:
QObject::startTimer: QTimer can only be used with threads started with QThread
The thing that baffles me is my program is single threaded. The goal of the class in question is to send POST data to a php page I have on my server. As soon as it tries to send the POST, I get that message. Here is my code.
#ifndef TRANSMISSIONS_H
#define TRANSMISSIONS_H
#include "name_spawn.h"
#include <QNetworkReply>
#include <QObject>
#include <QNetworkConfigurationManager>
class Transmissions : public QObject
{
Q_OBJECT
public:
Transmissions();
void Send(GeneratedData);
public slots:
void serviceRequestFinished(QNetworkReply*);
signals:
void configurationAdded(const QNetworkConfiguration);
void configurationChanged(const QNetworkConfiguration);
void configurationRemoved(const QNetworkConfiguration);
void onlineStateChanged(bool);
void updateCompleted();
};
#endif // TRANSMISSIONS_H
And
#include "transmissions.h"
#include "name_spawn.h"
#include <QHttp>
#include <QUrl>
#include <QString>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <iostream>
#include <QNetworkAccessManager>
#include <QNetworkConfigurationManager>
#include <QObject>
using namespace std;
Transmissions::Transmissions()
{
}
void Transmissions::Send(GeneratedData User)
{
cerr<<"transmitting"<<endl;
QUrl serviceUrl = QUrl("http://192.168.1.138/postTest.php");
QByteArray postData;
QString username="user="+User.Email()+"&";
QString Passwd="password="+User.pass();
postData.append(username);
postData.append(Passwd);
QNetworkAccessManager *networkManager = new QNetworkAccessManager(this);
connect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(serviceRequestFinished(QNetworkReply*)));
networkManager->post(QNetworkRequest(serviceUrl), postData);
}
void Transmissions::serviceRequestFinished(QNetworkReply *reply)
{
//this will do other things once post is working
QString data = reply->readAll();
cerr <<"Data is "<< data.toStdString()<<endl;
}
I think what I am trying to do is fairly simple, but it is frustrating me to no end trying to get it to work. I didn't see anything in the documentation about QNetworkAccessManager requiring threads. I admit I don't know Qt that well, so any help (or links to complete POST examples) would be very appreciated.
To use a QTimer you need to have an event loop. QNAM obviously uses a timer to periodically check for the network reply.
You need to start the application event loop with QCoreApplication::exec() and then call QNAM methods like post after that.
I think you can call post before exec but you may come across this bug.
Also, note that up to Qt 4.7 QNAM did not use threading but with 4.8 this is changing.
This may be related to creating the QNetworkAccessManager inside the Send method - try instead using RAII.
Define the QNetworkAccessManager in the header for Transmissions as a class variable, and create a class instance it in the ctor, then you will be able to post to it from the Send thread.
Otherwise I think it goes out of scope.
Related
I'm currently trying to make a software that downloads a lot of files from Google Drive. Downloading is currently not a problem.
Nevertheless, I encounter an issue when launching 500+ simultaneous downloads. I use a slightly modified version of this tutorial : https://wiki.qt.io/Download_Data_from_URL.
Here is the .h file :
class FileDownloader : public QObject
{
Q_OBJECT
public:
explicit FileDownloader(QUrl url, QObject *parent = 0, int number = 0);
QByteArray downloadedData() const;
void launchNewDownload(QUrl url);
QByteArray m_DownloadedData;
QNetworkReply* reply;
static QNetworkAccessManager *m_WebCtrl;
signals:
void downloaded();
private slots:
void fileDownloaded(QNetworkReply* pReply);
};
And here is the .cpp file :
QNetworkAccessManager* FileDownloader::m_WebCtrl = nullptr;
FileDownloader::FileDownloader(QUrl url, QObject *parent) :
QObject(parent)
{
if (m_WebCtrl == nullptr) {
m_WebCtrl = new QNetworkAccessManager(this);
}
connect(m_WebCtrl, SIGNAL (finished(QNetworkReply*)),this, SLOT (fileDownloaded(QNetworkReply*)));
launchNewDownload(url);
}
void FileDownloader::launchNewDownload(QUrl url) {
QNetworkRequest request(url);
this->reply = m_WebCtrl->get(request);
}
void FileDownloader::fileDownloaded(QNetworkReply* pReply) {
m_DownloadedData = pReply->readAll();
//emit a signal
pReply->deleteLater();
emit downloaded();
}
QByteArray FileDownloader::downloadedData() const {
return m_DownloadedData;
}
The issue is "QThread::start: Failed to create thread ()" when reaching about the 500th download. I tried to limit the number of downloads which run at the same time - but I always get the same issue. Besides, I tried to delete every downloader when finishing its task - it did nothing else than crashing the program ;)
I think that it is coming from the number of threads allowed for an only process, but I'm not able to solve it !
Does anyone have an idea that could help me ?
Thank you !
QNetworkAccessManager::finished signal is documented to be emitted whenever a pending network reply is finished.
This means that if the QNetworkAccessManager is used to run multiple requests at a time (and this is perfectly normal usage). finished signal will be emitted once for every request. Since you have a shared instance of QNetworkAccessManager between your FileDownloader objects, the finished signal gets emitted for every get call you have made. So, all the FileDownloader objects get a finished signal as soon as the first FileDownloader finishes downloading.
Instead of using QNetworkAccessManager::finished, you can use QNetworkReply::finished to avoid mixing up signals. Here is an example implementation:
#include <QtNetwork>
#include <QtWidgets>
class FileDownloader : public QObject
{
Q_OBJECT
//using constructor injection instead of a static QNetworkAccessManager pointer
//This allows to share the same QNetworkAccessManager
//object with other classes utilizing network access
QNetworkAccessManager* m_nam;
QNetworkReply* m_reply;
QByteArray m_downloadedData;
public:
explicit FileDownloader(QUrl imageUrl, QNetworkAccessManager* nam,
QObject* parent= nullptr)
:QObject(parent), m_nam(nam)
{
QNetworkRequest request(imageUrl);
m_reply = m_nam->get(request);
connect(m_reply, &QNetworkReply::finished, this, &FileDownloader::fileDownloaded);
}
~FileDownloader() = default;
QByteArray downloadedData()const{return m_downloadedData;}
signals:
void downloaded();
private slots:
void fileDownloaded(){
m_downloadedData= m_reply->readAll();
m_reply->deleteLater();
emit downloaded();
}
};
//sample usage
int main(int argc, char* argv[]){
QApplication a(argc, argv);
QNetworkAccessManager nam;
FileDownloader fileDownloader(QUrl("http://i.imgur.com/Rt8fqpt.png"), &nam);
QLabel label;
label.setAlignment(Qt::AlignCenter);
label.setText("Downloading. . .");
label.setMinimumSize(640, 480);
label.show();
QObject::connect(&fileDownloader, &FileDownloader::downloaded, [&]{
QPixmap pixmap;
pixmap.loadFromData(fileDownloader.downloadedData());
label.setPixmap(pixmap);
});
return a.exec();
}
#include "main.moc"
If you are using this method to download large files, consider having a look at this question.
One solution could be to uses a QThreadPool. You simply feed it tasks (QRunnable) and it will handle the number of threads and the task queue for you.
However in your case this is not perfect because you will be limiting the number of simultaneous downloads to the number of threads created by QThreadPool (generally the number of CPU core you have).
To overcome this you will have to handle the QThread yourself and not use QThreadPool. You would create a small number of thread (see QThread::idealThreadCount()) and run multiple FileDownloader on each QThread.
I can not get the QSoundEffect to play in a separate thread. Could you please tell me why is the sound played only by the first code snippet and not by the second?
//main.cpp
#include <QCoreApplication>
#include "SoundThread.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// #1
QSoundEffect alarmSound;
alarmSound.setSource(QUrl::fromLocalFile(":/sound"));
alarmSound.play();
/* #2
SoundThread thread;
thread.start();
thread.wait();
*/
return a.exec();
}
and
//SoundThread.h
#ifndef SOUNDTHREAD_H
#define SOUNDTHREAD_H
#include <QThread>
#include <QtMultimedia/QSoundEffect>
class SoundThread : public QThread
{
Q_OBJECT
private:
void run()
{
QSoundEffect alarmSound;
alarmSound.setSource(QUrl::fromLocalFile(":/sound"));
alarmSound.play();
while(true){}
}
};
#endif // SOUNDTHREAD_H
From the Qt documentation on QThread: -
By default, run() starts the event loop by calling exec()
Since you've inherited from QThread, you now have a run function which doesn't call exec(). Therefore, the event loop is not running and is most likely required for playing the sound effect.
Calling exec() should be substituted for the while(true){} as exec() will wait until exit() is called.
Doing it properly, with moving an object to the thread, based on "How to Really Truly Use QThreads..."
class Worker : public QObject
{
Q_OBJECT
public:
Worker();
~Worker();
public slots:
void PlaySoundEffect();
signals:
void finished();
void error(QString err);
private:
// store the sound effect, so we can reuse it multiple times
QSoundEffect* m_pAlarmSound;
private slots:
};
Worker::Worker()
{
m_pAlarmSound = new QSoundEffect;
m_pAlarmSound.setSource(QUrl::fromLocalFile(":/sound"));
}
Worker::~Worker()
{
delete m_pAlarmSound;
m_pAlarmSound = nullptr; // C++ 11
}
void Worker::PlaySoundEffect()
{
m_pAlarmSound->play();
}
// setup the worker and move it to another thread...
MainWindow::MainWindow
{
QThread* thread = new QThread;
Worker* worker = new Worker();
worker->moveToThread(thread);
connect(worker, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
connect(thread, SIGNAL(started()), worker, SLOT(PlaySoundEffect()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
// We can also connect a signal of an object in the main thread to the PlaySoundEffect slot
// Assuming MainWindow has declared a signal void Alert();
connect(this, &MainWindow::Alert, worker, &Worker::PlaySoundEffect);
// Then play the sound when we want: -
emit Alert();
}
While this seems like a lot of effort, there are many advantages of doing it this way. If, for example, you have a lot of sound effects, the method of inheriting QThread means that you're creating a thread per sound effect, which isn't ideal.
We could easily extend the above Worker object to hold a list of sound effects and play the one we want, by passing an enum into the PlaySoundEffect slot. As this thread is constantly running, playing sounds will incur less delay; it takes time and resources to create a thread at run-time.
You enter to an infinite loop at the end of your run function which causes to block the thread and consequently the QSoundEffect not working. It should be like:
void run()
{
QSoundEffect alarmSound;
alarmSound.setSource(QUrl::fromLocalFile(":/sound"));
alarmSound.play();
exec();
}
Replace while(true){} by QThread::run();, which will launch an internal event loop and will wait (sleep) for events, such as timer events and asynchronous signals calling slots, which is probably what is happening internally in QSoundEffect: When you call QSoundEffect::play() Some event (probably signals/slots) are queued in the event queue from within QThread, but nothing is processing the event queue. Remember: you are overriding virtual void run() and the original implementation was calling QThread::exec() for you. It is always a good idea to always call your super classes virtual function whenever you override them, as long as they are not pure virtual.
void run()
{
QSoundEffect alarmSound;
alarmSound.setSource(QUrl::fromLocalFile(":/sound"));
alarmSound.play();
QThread::run();
}
Some people suggested that calling QThread::exec() would do the trick. They may be right. I have to check the implementation of QThread to confirm that it is the only thing called in QThread::run() implementation. I personally think (by experience) that it is always safer to call your superclasse's virtual function in case there are other things called (other than QThread::exec() for this particular case).
Another option would be to move your QSoundEffect instance onto the thread and use signals and slots default auto-connection behaviour type to switch threads.
SoundPlayer.h:
#ifndef SOUNDPLAYER_H_
#define SOUNDPLAYER_H_
#include <QObject>
#include <QThread>
#include <QSoundEffect>
class SoundPlayer : public QObject
{
Q_OBJECT
public:
SoundPlayer();
signals:
void play();
private:
QThread m_thread;
QSoundEffect m_alarmSound;
};
#endif
SoundPlayer.cpp :
#include "SoundPlayer.h"
SoundPlayer()
{
m_alarmSound.setSource(QUrl::fromLocalFile(":/sound"));
m_alarmSound.moveToThread(&m_thread);
connect(this, SIGNAL(play()), &m_alarmSound, SLOT(play()));
m_thread.start(); // QThread::exec() will be called for you, making the thread wait for events
}
And then calling the play() signal would start playing in the correct thread.
SoundPlayer player;
emit player.play(); // m_alarmSound.play() will be called by m_thread
I have a main thread that loads the web-page and displays it. I have another thread running, that will just print the debug message on to the console.However, I am seeing that on running the QT-Thread, the web-page is not getting loaded. I tried putting the loading of web-page on to the constructor of thread, but this is also not helping here.Here is the code.
class MyJavaScriptOperations : public QObject {
Q_OBJECT
public:
Q_INVOKABLE qint32 MultOfNumbers(int a, int b) {
qDebug() << a * b;
return (a*b);
}
};
#if 1
class MyThread : public QThread
{
Q_OBJECT
public:
MyThread();
public:
void run();
};
MyThread::MyThread()
{
qDebug()<<"Constructor called";
QWebView *view = new QWebView();
view->resize(400, 500);
view->page()->mainFrame()->addToJavaScriptWindowObject("myoperations", new MyJavaScriptOperations);
view->load(QUrl("./shreyas.html"));
view->show();
this->run();
}
void MyThread::run()
{
qDebug()<<"Thread running";
while(1)
{
qDebug()<<"Fire Callback now";
}
}
#endif
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyThread t;
//t.run();
return a.exec();
}
Just because code is in a subclass of QThread doesn't mean that code is executed in that thread. Your main thread constructs the object, and that constructor calls run(). This means that the code of the run method is still executed in the main thread, and - as it is blocking - the line a.exec() is never called, and the main thread never gets an event loop which is required for paint events and the like.
What you need to do is start the thread and wait for run() being executed:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// TODO: Code for your web view goes here. You will probably need to
// pass the created web view into the constructor of MyThread
MyThread t;
// start the thread - this will put an event in the main event loop
t.start();
// start the event loop - this will lead to MyThread::run() being called
return a.exec();
}
This is enough to get your example running, but you will get errors when closing the web view as your way of using threads is not the intended one: If you want to make your code stable, put the code of your run() method in a separate worker class and use a default QThread for managing that, without subclassing QThread.
I recommend reading the Qt5 Documentation on threads, which is also applicable for earlier versions of Qt.
This question already has answers here:
QThread and QTimer
(4 answers)
Closed 8 years ago.
I have some very time consuming tasks to execute in a GUI application, and I want them to be threaded so it won't freeze my GUI..
To do so, I created a thread, and started a timer. I connected the timeout() method from my timer to the time-consuming function I want to paralellize, in the "run" method of my thread, but when the signal is called, the time-consuming function is called in the main thread instead of the one I created.
Here's my code, simplified for the example:
void MyThread::run()
{
m_pTimer = new QTimer()
connect(m_pTimer, SIGNAL(timeout()), this, SLOT(timeConsumingMethod()));
m_pTimer->start(x);
}
I printed the value of the static method QThread::currentThread() in "timeConsumingMethod", in MyThread::run() and in my main, and its value is the same as the one in my main.
How can I make my function run in the separated thread? And more important why is my code wrong?
"You’re doing it wrong." :)
try this:
myworker.h
#include ...
class MyWorker : public QObject {
Q_OBJECT
public:
explicit MyWorker(QObject* parent = 0);
public slots:
void timeConsumingMethod();
}
myworker.cpp
#include "myworker.h"
#include ...
MyWorker::MyWorker(QObject* parent) :
QObject(parent)
{
/*
...
*/
}
void MyWorker::timeConsumingMethod() {
/*
...
*/
}
code
/*...*/
MyWorker* worker = new MyWorker();
QThread* workerThread = new QThread();
QTimer* timer = new QTimer();
worker->moveToThread(workerThread);
worker->connect(timer, SIGNAL(timeout()), SLOT(timeConsumingMethod()));
// don't forget proper obj deletion
// not the best way, but...
worker->connect(workerThread, SIGNAL(destroyed()), SLOT(deleteLater());
workerThread->start();
timer->start(x);
in that case worker will "live" in workerThread. QT SIGNAL-SLOT mechanism supports connection between objects in different threads.
that approach much better than "procedural way" by reimplementing run() method and QThread inheritance
worker works, QThread controls thread
source: http://qt-project.org/wiki/Threads_Events_QObjects
I have a button in an simple android app that I am using to call a native function. The button in main activity calls a function of HADriver.java. This function in turn calls on a JNI function in DriverAdapter.cpp. That JNI function then in turn calls on a native function in Driver.cpp. The following are the parts of each of those files that comes into play when the button testCout is clicked in MainActivity.
Button in MainActivity:
private HADriver driver = new HADriver();
....
testCout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
driver.testCout();
}
});
HADriver.java
package com.ihearhtpi;
public class HADriver {
static {
System.loadLibrary("gnustl_shared");
System.loadLibrary("driveradapter");
}
public native void testCout();
}
DriverAdapter.cpp
#include <jni.h>
#include <string.h>
#include <android/log.h>
#include <Driver/driver.h>
#define DEBUG_TAG "NativeCalls"
void Java_com_ihearhtpi_HADriver_testCout(JNIEnv * env, jobject thiz)
{
Driver* driver = new Driver();
driver->testCoutFunc();
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:LC: [%s]", "Testing... this best effing work!");
delete driver;
}
Driver.cpp
#include <Driver/driver.h>
#include <iostream>
...
//bunch of custom irrelevant functions and includes
Driver::Driver()
{
CustomFuncs::initBitMasks(); // call global namespace function of CustomFuncs
}
void Driver::testCoutFunc()
{
std::cout << "These are the droids you're looking for." << std::endl;
}
However, I keep getting the following error and my app crashes each time I click the testCout button.
04-13 02:22:46.130: W/dalvikvm(17228): No implementation found for native Lcom/ihearhtpi/HADriver;.testCout:()V
04-13 02:22:46.130: D/AndroidRuntime(17228): Shutting down VM
04-13 02:22:46.130: W/dalvikvm(17228): threadid=1: thread exiting with uncaught exception (group=0x40e0c300)
04-13 02:22:46.130: E/AndroidRuntime(17228): FATAL EXCEPTION: main
04-13 02:22:46.130: E/AndroidRuntime(17228): java.lang.UnsatisfiedLinkError: Native method not found: com.ihearhtpi.HADriver.testCout:()V
04-13 02:22:46.130: E/AndroidRuntime(17228): at com.ihearhtpi.HADriver.testCout(Native Method)
What gives? I can't figure out why this native call is not working!
Apparently in my JNI file I needed to wrap all functions with
#ifdef __cplusplus
extern "C" {
#endif
...JNI functions here...
#ifdef __cplusplus
}
#endif
After this, everything is working fine
try not calling testCout through driver. try to make a wrapper function so something like driver.testCoutHelper(); in the onclick.
then in testCoutHelper() (within HADriver.java) can you call testCout(); Lets try this first and tell me the results;