I have wrote an app in QT/C++. This app have multiple classes to manage window, treewidget.. and a custom class.
The goal of the app is to be android file transfer -like in QT/c++ on MacOSx.
Currently the entire app is working in one thread which include the UI management and the android device management. The android device access is managed by a class named DeviceManager. This class will mostly open the device, read it, add/delete files....
What I want to do is to create a thread which will handle all method defined in the DeviceManager. I want the UI on one thread and the devicemngr in a separate thread.
Here is my current code :
main.cpp
int main(int argc, char *argv[])
{
PULS_mtp_error_t error = ERROR_GENERAL;
QThread PulsDeviceThread;
QApplication app(argc, argv);
DeviceMngr *MyMtp = new DeviceMngr;
error = MyMtp->OpenDevice();
...
MainUI MyWindow(*MyMtp);
MyWindow.show();
return app.exec();
}
the MainUI class is defined as below
MainUI::MainUI(DeviceMngr& device) :
m_device(device)
{
m_closing = false;
setWindowTitle(QString::fromUtf8("PULS"));
resize(800,600);
setUnifiedTitleAndToolBarOnMac(true);
/* Creation of the Top bar section */
createBackForwardButton();
createLogoSection();
createAddFolder();
QWidget *TopBarWidget = new QWidget();
TopBarWidget->setFixedHeight(61);
QHBoxLayout *TopBarLayout = new QHBoxLayout(TopBarWidget);
TopBarLayout->addWidget(BackForwardSection);
TopBarLayout->addWidget(LogoSection);
TopBarLayout->addWidget(AddFolderSection);
/* Creation of Tree View Section */
createTreeView();
/* Creation of the bottom bar section */
createInfoSection();
/*about*/
aboutAction = new QAction(tr("&About"),this);
connect(aboutAction, SIGNAL(triggered()),this ,SLOT(aboutPuls()));
QMenu *helpMenu = new QMenu("Help", this);
helpMenu->addAction(aboutAction);
menuBar()->addMenu(helpMenu);
/*Overall Layout*/
QWidget *MainWindowWidget = new QWidget();
QVBoxLayout *MainWindowLayout = new QVBoxLayout(MainWindowWidget);
MainWindowLayout->setSpacing(0);
MainWindowLayout->addWidget(TopBarWidget);
MainWindowLayout->addWidget(TreeViewSection);
MainWindowLayout->addWidget(CreateInfoSection);
setCentralWidget(MainWindowWidget);
PulsUnplugged = false;
#if 1
activeTimer = new QTimer(this);
activeTimer->setSingleShot(false);
activeTimer->setInterval(200);
connect(activeTimer, SIGNAL(timeout()), this, SLOT(PulsDetection()));
activeTimer->start();
#endif
show();
}
Some of the method such as createTreeView will use the m_device to access also to the device.
void MainUI::createTreeView()
{
TreeViewSection = new QWidget();
QVBoxLayout *TreeViewLayout = new QVBoxLayout(TreeViewSection);
MyTreeWidget = new MyNewTreeWidget(m_device, *this);
TreeViewLayout->addWidget(MyTreeWidget);
}
MyNewTreeWidget will also need to access to the DeviceMngr class.
Most of class used for the UI management can access to the DeviceMngr class. I don't know how to use the QThread to make sure that all UI classes can access to the DeviceMngr class.
I was thinking to create a Qthread in the main.cpp but I do not see how to add slots/signals in the main.cpp and DeviceMngr will have signals/slots to discuss with all other thread. the main will need for example to open the device and receive the result.
Do I need to create all signal/slot connection in the main or I can just add what I need in the different classes and create the connections when needed.
Any idea ? I have tried a first implementation but it not really working fine.
Thanks
I would suggest creating the worker thread and moving your DeviceMngr to it. Its slots (and whole event loop) will run in the context of the thread and you must use Qt's signal/slot mechanism that will ensure thread safe access to DeviceMngr from other QObjects.
int main(...) {
// ...
QThread MtpThread;
DeviceMngr MyMtp;
MyMtp.moveToThread(&MtpThread);
// connect signals/slots of DeviceMngr
// ...
// launch the thread
MtpThread.start();
// should you need to call slots of DeviceMngr from main use metacalls
QMetaObject::invokeMethod(&MyMtp, "nameOfSlot");
// run application
// in the end join
MtpThread.quit(); // stop event queue
MtpThread.wait(); // join the thread
}
I hope you get the idea. Key is moveToThread() and metacalls.
Related
Is it possible to initiate the object of MainWindow in QT and call the show method from another class, for example inside a thread class constructor, and then create the thread object in the main function and start the thread? It does not work for me (the ui flashes and disappears immediately). Is there any way to fix it? If not, how can I control buttons and other stuff from the thread. How can I communicate between them (the thread and ui class) by exchanging data?
My program looks like this. Thread constructor:
#include "mainwindow.h"
void thread::thread(){
MainWindow m;
m.show();
}
and the main function:
int main(int argc,char *argv[]){
QApplication a(argc,argv);
thread t;
t.start();
return a.exec();
}
You can do this, but before that you have to connect your MainWindow to the thread object. You should never call from another thread than the GUI-thread functions that modify the GUI.
The solution would be to use slots/signals. All the communication from threads to the UI is always done with slots and signals.
Below an example:
Create a signal in your Thread class called mySignal();
class Thread {
...
signals:
void mySignal();
...
}
and create a slot in your MainWindow
class MainWindow {
...
public slots:
void ShowWindow();
..
}
Create a function in your MainWindow class called showWindow()
void MainWindow::ShowWindow() {
this->show();
}
Connect your thread to the MainWindow
connect(threadObject, &Thread::mySignal, this, &MainWindow::ShowWindow);
Call the slot from your thread with emit mySignal();
PS: Your application should be a widget application not console.
I am programming with Graphics View Framework. I use a customized class which inherit from QGraphicsView to show QGraphicsScene. I new QGraphicsScene in main thread and new QGraphicsItem in another thread. When I call scene.addItem()--here QT will emit signal to scene and it is not allowed by QT. But if I write setMouseTracking(true) in my QGraphicsView that would correct the QT's error. Why?
My code:
//centralWidget.cpp
pGraphicsScene_ = new QGraphicsScene(this);
pMonitorView_ = new MonitorView(pGraphicsScene_);
//monitorView.cpp
MonitorView::MonitorView(QGraphicsScene *scene, QWidget *parent):
QGraphicsView(scene, parent)
{
setMouseTracking(true);//If I comment this line,will get error--for this I don't confuse but write this statement will correct error!
}
//another thread start by std::thread
pItem = new GoodsItem();
pGraphicsScene_->addItem(pItem);
I'm trying the add threading in my app write in C++ and QT.
I have a class Framework and one name DeviceMngr.
The framework.h is defined as below:
class Framework : public QObject
{
Q_OBJECT
QThread FrameWorkThread;
public:
Framework();
The framework is initialized by the main. My Main is just doing :
QApplication app(argc, argv);
QThread FrameWorkThread;
Framework *DeviceFramework = new Framework;
DeviceFramework->moveToThread(&FrameWorkThread);
QObject::connect(&FrameWorkThread, SIGNAL(finished()), DeviceFramework, SLOT(deleteLater()));
After, the main in it the main Windows and give the DeviceFramework as argument.
MainUI MyWindows(*DeviceFramework);
MyWindows is discussing with DeviceFramework using Signal/slots.
Framework based is access to an android device using class DeviceMngr and methode.
How is it possible for me to add the DeviceMngr in the same Thread than the Framework.
Can I do something like this in the framework.cpp:
Framework::Framework()
{
Device = new DeviceMngr();
Device->moveToThread(&FrameWorkThread);
}
And the device manager declared as below :
class DeviceMngr : public QObject
{
QThread FrameWorkThread;
public:
DeviceMngr();
~DeviceMngr();
Is this method place the framework and device manager in the FrameWorkThread ?
Thanks
Sebastien
It is possible to have your DeviceMngr and Framework instances in the same thread. To do this, you'll need to keep the QThread instance you want to have in common between them in one place (or pass a pointer).
Do you have a reason for not making your DeviceMngr instance a child of Framework? The documentation for QObject says the following about the thread affinity of a child of a QObject instance:
The QObject::moveToThread() function changes the thread affinity for
an object and its children (the object cannot be moved if it has a
parent).
http://doc.qt.io/qt-5/threads-qobject.html
This would be the simplest way of getting both objects on the FrameWorkThread.
main.cpp
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QThread FrameWorkThread;
Framework *DeviceFramework = new Framework();
DeviceFramework->moveToThread(&FrameWorkThread);
QObject::connect(&FrameWorkThread, SIGNAL(finished()), DeviceFramework, SLOT(deleteLater()));
}
framework.hpp
class Framework : public QObject
{
Q_OBJECT
public:
Framework();
}
framework.cpp
Framework::Framework()
{
Device = new DeviceMngr(this);
}
Problem
I want to use a QTimer to update a derived QSplashScreen that draws a progress bar (using paint commands, not a widget) to estimate when the program will start running.
By necessity, this happens prior to the exec call of the QCoreApplication. I've gotten this to work (in release mode only) on both X11 and Windows, by putting a timer in a second thread, and calling a function in the splash screen which updates the progress and repaints the widget. However, this doesn't work in debug mode as it produces the following error:
"ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread."
I'm not really worried about this assertion as the code doesn't crash in release and it's just a splash screen, however I need to be able to run the program in debug so I'd like to either a) refactor the code so it doesn't trigger the assertion, or b) dissable this particular assertion.
Tried:
Using update() instead of repaint(). This doesn't cause an assertion, but it also doesn't repaint because the main thread is too busy loading in the shared libraries, etc, and the timer events don't get processed until I'm ready to call finish on the splash screen.
starting QTimer in main loop. Same result as above.
Using a QT::QueuedConnection. Same result.
Main
#include <QApplication>
#include <QtGui>
#include <QTimer>
#include <QThread>
#include "mySplashScreen.h"
#include "myMainWindow.h" // contains a configure function which takes a
// LONG time to load.
int main( int argc, char* argv[] )
{
// estimate the load time
int previousLoadTime_ms = 10000;
QApplication a(argc, argv);
MySplashScreen* splash = new MySplashScreen(QPixmap(":/splashScreen"));
// progress timer. Has to be in a different thread since the
// qApplication isn't started.
QThread* timerThread = new QThread;
QTimer* timer = new QTimer(0); // _not_ this!
timer->setInterval(previousLoadTime_ms / 100.0);
timer->moveToThread(timerThread);
QObject::connect(timer, &QTimer::timeout, [&]
{
qApp->processEvents(); splash->incrementProgress(1);
});
QObject::connect(timerThread, SIGNAL(started()), timer, SLOT(start()));
timerThread->start();
splash->show();
a.processEvents();
myMainWindow w;
QTimer::singleShot(0, [&]
{
// This will be called as soon as the exec() loop starts.
w.configure(); // this is a really slow initialization function
w.show();
splash->finish(&w);
timerThread->quit();
});
return a.exec();
}
Splash Screen
#include <QSplashScreen>
class MySplashScreen : public QSplashScreen
{
Q_OBJECT
public:
MySplashScreen(const QPixmap& pixmap = QPixmap(), Qt::WindowFlags f = 0)
: QSplashScreen(pixmap, f)
{
m_pixmap = pixmap;
}
virtual void drawContents(QPainter *painter) override
{
QSplashScreen::drawContents(painter);
// draw progress bar
}
public slots:
virtual void incrementProgress(int percentage)
{
m_progress += percentage;
repaint();
}
protected:
int m_progress = 0;
private:
QPixmap m_pixmap;
};
MyMainWindow
#include <QMainWindow>
class myMainWindow : public QMainWindow
{
public:
void configure()
{
// Create and configure a bunch of widgets.
// This takes a long time.
}
}
The problems are because the design is backwards. The GUI thread should not be doing any loading. The general approach to GUI threads is: do no work in the GUI thread. You should spawn a worker thread to load what you need loaded. It can post events (or invoke slots using a queued connection) to the GUI thread and its splash screen.
Of course, the worker thread should not create any GUI objects - it can't instantiate anything deriving from QWidget. It can, though, instantiate other things, so if you need any expensive-to-obtain data, prepare it in the worker thread, and then cheaply build a QWidget in the GUI thread once that data is available.
If your delays are due to library loading, then do load all the libraries in a worker thread, explicitly, and ensure that all of their pages are resident in memory - for example by reading the entire .DLL after you're loaded it as a library.
The MyMainWindow::configure() could be called in a worker thread, as long as it doesn't invoke any QWidget methods nor constructors. It can do GUI work, just not visible on screen. For example, you can load QImage instances from disk, or do painting on QImages.
This answer provides several approaches to executing a functor in a different thread, GCD-style.
If you are constructing widgets that are expensive to construct, or construct many of them, it's possible to make sure that the event loop can run between the instantiation of each widget. For example:
class MainWindow : public QMainWindow {
Q_OBJECT
QTimer m_configureTimer;
int m_configureState = 0;
Q_SLOT void configure() {
switch (m_configureState ++) {
case 0:
// instantiate one widget from library A
break;
case 1:
// instantiate one widget from library B
...
break;
case 2:
// instantiate more widgets from A and B
...
break;
default:
m_configureTimer.stop();
break;
}
}
public:
MainWindow(QWidget * parent = 0) : QMainWindow(parent) {
connect(&m_configureTimer, SIGNAL(timeout()), SLOT(configure()));
m_configureTimer.start(0);
//...
}
};
I have an application in which each thread (except the main thread) needs to create its own window. I tried creating a thread and then calling this->exec() in the run function. However, I get an error before I even get to that call: ASSERT failure in QWidget: "Widgets must be created in the GUI thread."
I want to popup a message window. The problem is that the source has multiple threads each of which may need to popup its own message.
If you need to create QWidget(or some other gui component(s)) in different(non-main) thread(s) you can implement it in such way:
Create simple wrapper which holds gui component:
// gui component holder which will be moved to main thread
class gui_launcher : public QObject
{
QWidget *w;
// other components
//..
public:
virtual bool event( QEvent *ev )
{
if( ev->type() == QEvent::User )
{
w = new QWidget;
w->show();
return true;
}
return false;
}
};
create QApplication object in main thread
another thread body:
..
// create holder
gui_launcher gl;
// move it to main thread
gl.moveToThread( QApplication::instance()->thread() );
// send it event which will be posted from main thread
QCoreApplication::postEvent( &gl, new QEvent( QEvent::User ) );
..
be happy, :)
Qt will only let you create GUI elements in the GUI thread - what is it that you need to display from the other threads? See something like This answer for an example of updating a progress bar with data from a non-GUI thread.
Update:
If you want to show a message for each window, you can have a class like this:
class MyWorkerThread : public QThread
{
Q_OBJECT
signals:
void sendMessage(QString msg);
private:
void run()
{
/* do stuff */
emit sendMessage(QString("This thread is doing stuff!"));
/* do more stuff */
}
};
Then connect it up to your GUI via the signal-slot mechanism with something like:
connect(workerThread, SIGNAL(sendMessage(QString)),
guiController, SLOT(showMessageBox(QString)));
Where the showMessageBox function does what you need it to do.
I don't believe this is possible. Other non-GUI components can run in other threads and will usually communicate via the signal/slot mechanisms.
The above answers can be combined with a QAction object (or custom class objects) to transfer any action to the main GUI thread to be executed, not just creating widgets or displaying a message box. (e.g. by emitting sendAction(QAction*), or implementing a custom QEvent class embodying a QAction*.)