I've found this, but it is only about QWidget based project. What about ApplicationWindow components in QML?
Finally I've connected to frameSwapped signal of the main top-level QQuickWindow of my application. It is called right after each repainting is done. So, after first repainting, my slot would be called, and I will start to really load the data (what is rather slow). Inside this slot I'm destroying this connection, so I don't slow down the application.
//main.cpp
QQuickWindow* mainWindow =
qobject_cast<QQuickWindow*>(engine.rootObjects().first());
QMetaObject::Connection loadingFinished =
QObject::connect(mainWindow, SIGNAL(frameSwapped()),
&controller, SLOT(construct()));
controller.setConnection(loadingFinished);
//Controller.cpp
void Controller::construct() // this is slot
{
// some really long operation
disconnect(*m_loadingFinished);
}
Hope it will be helpful for someone.
Related
In my project, I want to call a variable QImage image generated in the mainwindow.h, in other class files, e.g., called SegSetupDialog.h. Here the image is loaded by clicking a pushbutton in mainwindow.ui, and the SegSetupDialog is a QDialog, which pops up by clicking one pushbutton in mainwindow.ui.
I tried to use a signal-slot connection to send qimage of mainwindow to SegSetupDialog as follows.
For Class MainWindow:
SegSetupDialog *segsetup;
if(image.isNull()==false)
{
emit sendImgData(image);
qDebug()<<"sendImgData emitted!";
if(segsetup==NULL) segsetup = new SegSetupDialog();
connect(this, SIGNAL(sendImgData(QImage)),segsetup,SLOT(getImgData(QImage)),Qt::QueuedConnection);
}
In SegSetupDialog::getImgData
void SegSetupDialog::getImgData(QImage qimage)
{
qImg = qimage;
qDebug()<<"qimage received!";
}
The above connection seems not to work since the qDebug message in getImgData is not printed out. Anyone can help check something wrong with codes, or suggest other methods for accessing image of mainwindow? Thanks!!
You need to do the signal/slot connection before you emit the signal. Connections are done once, usually in the constructor.
However, you should probably do the connect() in the constructor of SegSetupDialog instead of the MainWindow one. It's SegSetupDialog that wants to be notified about image data updates, so you should establish the connection there.
Also, to make sure the signals and slots are specified correctly, don't use Qt 4 connect() calls. Use Qt 5 ones that are checked at compile-time:
connect(this, &MainWindow::sendImgData,
segsetup, &SegSetupDialog::getImgData, Qt::QueuedConnection);
(Of course change it appropriately if you move it to the SegSetupDialog constructor.)
I'm trying to enable a QPushButton after another QPushButton is clicked and I've run into a problem. The first QPushButton can emit a clicked() signal while the second QPushButton only has slots of the form setEnabled(bool) and setDisabled(bool).
Basically, I'm trying to do
connect(ui->pbViewVolume, SIGNAL(clicked()),
ui->pbSaveAsImage, SLOT(setEnabled(true)));
Since it's not possible to send a signal with fewer parameters than the slot, how can I best do this? The only way I see is to create a public slot for my MainWindow like
void EnableSaveAsImageButton(){
ui->pbSaveAsImage->setEnabled(true);
}
but I'd rather not fill my MainWindow with this sort of rubbish function.
You have to create that slot.
You don't need to make that slot public, make it private.
This function is not rubbish.
I'm working with QThread and slots/signals mechanism; I know there's a lot of threads about this on the Web in general and here at SO in particular, but I still could not find a solution. Anyway, here is the context.
The piece of code I am trying to come up with is aimed at controlling though the GUI an eventually long process, hence the use of a QThread.
I have a window with two buttons, start and stop. My Window also has a QThread and Task, where the latter inherits from a QObject. I do want to be able to stop my task while it is running, and to prevent starting it again if start is clicked while it is already running.
Here is an excerpt of Task (which fakes the long process):
class Task: public QObject
{
public:
Task(): QObject(), stop_(true) {}
private slots:
void startTask()
{
stop_ = false;
run();
}
void stopTask()
{
stop_ = true;
}
void run() const
{
while ( ! stop_)
{
sleep(1);
}
}
bool stop_;
};
I made two connections between the buttons and the task in the constructor of my Window:
class Window: public QWidget
{
public:
Window()
{
// Instantiate buttons and put them in a layout.
// ...
connect(buttonStart_, SIGNAL(clicked()), &task_, SLOT(startTask()));
connect(buttonStop_, SIGNAL(clicked()), &task_, SLOT(stopTask()),
Qt::DirectConnection);
task_.moveToThread(&thread);
thread_.start();
}
private:
QPushButton buttonStart_;
QPushButton buttonStop_;
QThread thread_;
Task task_;
};
I used Qt::DirectConnection in the second connect() in order to "force" processing of my signal requesting to stop the task, as (as I understand) task_ needs to return from its work before processing events further (if I use the default connection, all my clicks are processed after my task is "done").
Here, the Qt::DirectConnection "bypasses" the event queue, and that way I can stop my task. But to be honest I don't know if that is the proper way to do it, or if it is a workaround (thus possibly the root of my problem).
Anyway, this works OK that way, but when I start playing with my buttons, the GUI gets eventually frozen, and that's my problem!
Any help is welcome; thanks for your time!
Using a DirectConnection means that the slots will be executed in the UI thread (while your task thread is still running). This is not what you want. The way to handle this is to use an event loop in your thread, by running with QThread::exec().
In order for your thread to be able to respond as you want, you will need to ensure that the thread is able to process incoming events. There are a few ways to deal with this. One would be to call QCoreApplication::processEvents() occasionally while your task is running. Another is to use a QTimer connected to a slot which performs some processing. The important thing is to make sure that the event loop in your thread can run.
The first thing you need to understand is which thread your signal/slot connections are being executed in.
The default behavior in this particular case is to use a Qt::QueuedConnection, which will put your signal event onto the receiving thread's event queue. In the case of a Qt::DirectConnection, the slot is executed in the thread of the object that emitted the signal.
Using a Qt::DirectConnection would only be safe in this instance if you put a mutex around your stop_ variable to prevent both threads from accessing it at the same time.
I am trying to create a client application in QT which requires both threads for processing and a variable amount of window instances. But I am having a hard time trying to figure out how to create a new window inside one of the processing thread. I understand that all ui elements must be created in the same thread as the QApplication class, but I need to be able to instantiate, or at least have a reference to a QDialog in another thread.
Communicating between the thread and QDialog can be done using signals, I am not worried about this, but actually creating the window is another matter. I could use signals to tell the main thread to create an instance to the window, and then retrieve the pointer to it somehow, but to me that seems a bit to complicated and ugly. Is there a better way to accomplish such a task? To create a QDialog outside the main thread were the QApplication class exists?
Edit : I have tried the Q_INVOKABLE method but it does not work across threads. I have created a view factory class which can create a QDialog of a type I specify and returns a pointer to it. This class has been instantiated in the main GUI thread and a reference to this class is sent to any worker threads. The problem is that, when a thread invokes the create method from the factory using Qt::BlockingQueuedConnection, the invoke method fails. If I change it to Qt::DirectConnection, the invoke method calls the right create method but in the current thread as the worker thread.
My main function looks like this :
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ViewFactory vFactory;
vFactory.registerCreator(Util::W_CONNECT, new ConnectWindow::ConnectCreator());
ClientApp app;
if(!app.Initialize(&vFactory))
return 0;
app.start();
a.exec();
.............................
}
And my run function from the ClientApp thread looks something like this :
void ClientApp::run()
{
QDialog * tmp = NULL;
QMetaObject::invokeMethod(this->_vFactory, "create", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QDialog*, tmp), Q_ARG(int, 0));
}
Like I said, the invokeMothod will not fail if I change the connection type to Qt::DirectConnection, so the params are not the problem, but rather calling the method across a separate worker thread.
You can only do Gui stuff in the gui thread. The obvious solution is for the worker thread to send a message to the gui thread = a signal in Qt terms.
If a worker thread needs to ask a question it should send a message to the gui thread and then block until it gets a signal back.
AFAIK, signals (or just a dynamically callable method, using Q_INVOKABLE) or an event is the way to go.
Note that, using QMetaObject::invokeMethod() (with Qt::BlockedConnection), you can call a function safely across threads and get a return value back without too much coding.
It seems like QObject::moveToThread can solve this problem. This function moves event processing loop to another thread.
Example from Qt documentation:
myObject->moveToThread(QApplication::instance()->thread());
I am working on QT GUI project. In this application I have a QWidget as main window. I make the cursor from data coming from some source. When I set the cursor of widget. It gives me the following error.
QPixmap: It is not safe to use pixmaps outside the GUI thread
My code is as follows
void ImageWindow::setMouseCursor(unsigned char* data,unsigned char* maskbits,unsigned int length,int xHotSpot, int yHotSpot)
{
QBitmap bitmapData;
QBitmap bitmapMaskData;
bitmapData.loadFromData(data,length);
bitmapMaskData.loadFromData(maskbits,length);
this->setCursor(QCursor(bitmapData,bitmapMaskData,xHotSpot,yHotSpot));
this->update();
}
Function setMouseCursor is called from other class, which set the data of cursor.
ImageWindow is my customized QWidget class.
Apparently, the object which calls setMouseCursor lives outside the GUI thread as far as i can tell. In order to avoid this, make setMouseCursor a slot. Do not call the slot directly, instead emit a signal from the caller object, and connect that signal to setMouseCursor slot using Qt::QueuedConnection.
See : ConnectionType
Two problems:
don't use a QBitmap outside the GUI-thread
don't call gui objects setCursor outside the GUI-thread
Creating a Paint Device
One advantage of using QImage as a
paint device is that it is possible to
guarantee the pixel exactness of any
drawing operation in a
platform-independent way. Another
benefit is that the painting can be
performed in another thread than the
current GUI thread.