I am going to send data collected in the QMainWindow to an object of QDialog via signal-slot connections. Here is the relevant code fragment to do this in mainwindow.cpp:
void MainWindow::on_dialogA_clicked()
{
if(dialogA==NULL) dialogA =new MyDialog();
//'this' refers to MainWindow
connect(this,SIGNAL(sendData(QVector<bool>)), dialogA, SLOT(getData(QVector<bool>)), Qt::QueuedConnection);
dialogA->show();
}
However when working with dialogA, it seems that the data are not updated properly and the dialog interface becomes Not responding after while. I wonder if the signal-slot connection above is right or not, or that is the way to have data communication with a QDiaglog.
Two things...first, switch to the modern method of creating signal/slot connections:
connect (this, &MainWindow::sendData, dialogA, &MyDialog::getData, Qt::QueuedConnection);
If there's something wrong with the definition, using this format allows the compiler to catch it rather than a run-time warning. Assuming the parameters are defined correctly, there's nothing wrong with the "connect" statement except that it's in the wrong place, which is the second issue.
Every time the user clicks, an additional connection is being made between your main window and the dialog. Qt doesn't automatically ensure that only one connection is made between a given signal and slot. It'll create as many as you ask it for. The "connect" call should be part of the "if" block:
if (! dialogA)
{
dialogA =new MyDialog();
connect...
}
Depending on how much data is in that vector and what the dialog does with it, if you click enough times, it may be that you're just processing the data so many times that everything slows down tremendously.
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 have a problem with slots and signals. I created buttons and connected them to the clicked() slot. Then i decided to connect signals and slots manually and since then when I click the button it calls its function twice.
connect(ui->okButton, SIGNAL(clicked()), this, SLOT(on_okButton_clicked()));
void settingswindow::on_okButton_clicked()
{
qDebug() << "ok clicked";
this->close();
}
I was looking for the answer on google, but all i found was this: Where is the generated code of qt signals slots editor but my *.ui file looks like this: pastebin to the code. As you can see there's only one line with and nothing more. I can't find where the information about signals and slots is saved. Rebuild and clean options won't help.
This is not a bug in Qt. If you look at the generated code for your ui_*.h file, you'll notice that the last statement executed in the setupUi() function is a call to QMetaObject::connectSlotsByName().
Since your slot already conforms to the naming convention that this function is looking for, your slot is automatically connected to the signal.
By manually connecting the signal to the slot, in your settingswindow class, you effectively duplicate the connection.
As #Devopia mentioned, this is a documented feature.
I connect a slot with a signal. But now I want to disconnect them temporarily.
Here is part of my class declaration:
class frmMain : public QWidget
{
...
private:
QTimer *myReadTimer;
...
private slots:
void on_btnDownload_clicked();
...
};
In the constructor of frmMain, I connect myReadTimer with a slot so that ReadMyCom will be called every 5 seconds:
myReadTimer=new QTimer(this);
myReadTimer->setInterval(5000);
connect(myReadTimer,SIGNAL(timeout()),this,SLOT(ReadMyCom()));
But, in slot on_btnDownload_clicked. I don't want myReadTimer to emit any signal in on_btnDownload_clicked's scope. So I want to disconnect them at the beginning of on_btnDownload_clicked and reconnect them in the end. Like this:
void frmMain::on_btnDownload_clicked()
{
//some method to disconnect the slot & singal
...//the code that I want myReadTimer to leave me alone
//some method to reconnect the slot & singal
}
I searched in Stackoverflow and got some answer like call the QObject destructor. But I don't know how to use it.
I also tried to use disconnect, like:
QMetaObject::Connection myConnect;
myConnect=connect(myReadTimer,SIGNAL(timeout()),this,SLOT(ReadMyCom()));
...
disconnect(& myConnect);
But it still not work. So could any one help me how to do this?
There is a very nice function in QObject that comes in handy every now and again: QObject::blockSignals()
Here's a very simple fire-and-forget class that will do what you want. I take no credit for it's design, I found it on the internet somewhere a long time ago. Be careful though, it will block all signals to all objects. If this is not what you want, you can modify the class to suit your needs.
class SignalBlocker{
public:
SignalBlocker(QObject *o): object(o), alreadyBlocked(object->signalsBlocked()){
if (!alreadyBlocked){
object->blockSignals(true);
}
}
~SignalBlocker() {
if (!alreadyBlocked){
object->blockSignals(false);
}
}
private:
QObject *object;
bool alreadyBlocked;
};
Usage, in your case, becomes trivial
void frmMain::on_btnDownload_clicked()
{
SignalBlocker timerSignalBlocker(myReadTimer);
...//the code that I want myReadTimer to leave me alone
// signals automatically unblocked when the function exits
}
UPDATE:
I see that from Qt 5.3, a very similar class has been offically added to the API. It does a similar job as the one above with a slightly bigger feature-set. I suggest you use the official QSignalBlocker class instead in order to keep your codebase up-to-date with any API changes.
Usage, however, remains exactly the same.
Disconnect/reconnect syntax
There are many ways to call disconnect, depending on exactly what you want disconnected. See the QObject documentation page for an explanation of how they work.
Here's an example using 0 to mean "disconnect all slots."
void frmMain::on_btnDownload_clicked()
{
// disconnect everything connected to myReadTimer's timeout
disconnect(myReadTimer, SIGNAL(timeout()), 0, 0);
...//the code that I want myReadTimer to leave me alone
// restore the connection
connect(myReadTimer,SIGNAL(timeout()),this,SLOT(ReadMyCom()));
}
Or you can specify the exact signal-slot pair to disconnect by copying your 'connect' syntax, like this:
disconnect(myReadTimer,SIGNAL(timeout()),this,SLOT(ReadMyCom()));
Stopping the timer
Since you're working with a timer, this may be simpler:
void frmMain::on_btnDownload_clicked()
{
// stop the timer (so you won't get any timeout signals)
myReadTimer->stop();
...//the code that I want myReadTimer to leave me alone
// restart the timer (using whatever interval was set previously)
myReadTimer->start();
}
Differences from your original approach:
Since you're stopping and restarting the timer, the next time it fires will be interval after your slot function finishes.
Do you need to do anything special at all?
In a single-threaded Qt application, if you're already handling a signal, another signal won't "jump in the middle" of that code. Instead it'll be queued up as an even to handle immediately after the current slot returns.
So perhaps you don't need to stop or disconnect your timer at all.
Differences from your original approach:
If on_btnDownload_clicked takes a while to execute, you might have multiple ReadMyCom events queued up after on_btnDownload_clicked completes. (Note that at this point you'd have an operation that basically "locks up" your GUI for a while anyway; it may make more sense to refactor the function or give it its own thread.)
I have 2 classes: one maintains some loop (at leas for 2-3 minutes; and is inherited from QObject) and another shows up a progress dialog (inherited from QDialog).
I want to start the loop as soon as the dialog is shown. My first solution was:
int DialogClass::exec()
{
QTimer::singleShot(0, LoopClassPointer, SLOT(start()));
return __super::exec();
}
There is a problem with throwing exceptions from slots. so I considered a possibility to make public slot start() just a public function. But now I don't know how to make it works well. Things like this:
int DialogClass::exec()
{
LoopClassPointer->start();
QApplication::processEvents();
return __super::exec();
}
don't help. The dialog doesn't appears.
Is there a common approach to this kind of situations?
some details, according to questions:
I have to work with system with its own styles, so we have a common approach in creating any dialogs: to inherit them from stytle class, which is inherited from QDialog.
my 'LoopClassPointer' is an exported class from separate dll (there is no UI support in it).
I have a 'start' button in main app, which connected with a slot, which creates progress dialog and 'LoopClassPointer'. at the moment I send 'LoopClassPointer' instance in the dialog and don't whant to make significant changes in the architecture.
Take a look at QtDemo->Concurrent Programming->Run function
e.g. in Qt 4.8: http://qt-project.org/doc/qt-4.8/qtconcurrent-runfunction.html
In this situation, I recommend you separate the logic of the loop from the dialog. Gui elements should always be kept separate.
It's great that your worker class is derived from QObject because that means you can start it running on a separate thread: -
QThread* m_pWorkerThread = new QThread;
Worker* m_pWorkerObject = new Worker; // assuming this object runs the loop mentioned
// Qt 5 connect syntax
connect(m_pWorkerThread, &QThread::started, m_pWorkerObject, &WorkerObject::start);
connect(m_pWorkerThread, &QThread::finished, m_pWorkerThread, &QThread::deleteThis);
m_pWorkerObject->moveToThread(m_pWorkerThread);
m_pWorkerThread->start();
If you're not familiar with using QThread, then start by reading this.
The only other thing you require is to periodically send signals from your worker object with progress of its work and connect that to a slot in the dialog, which updates its display of the progress.
MyWindow which inherits from QMainWindow. MyWindow contains a QGLWidget that displays an animation.
The problem is that the animation pauses whenever I open a menu or resize the window.
The animation is implemented by calling QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)) periodically, then calling redrawing each time the window receives the QEvent::UpdateRequest, like this:
bool MyWindow::event(QEvent *event)
{
qDebug() << event;
switch (event->type())
{
case QEvent::UpdateRequest:
render();
return true;
default:
return QMainWindow::event(event);
}
}
As seen from qDebug(), while a menu is open or the window is being resized, the window stops receiving update request events.
Is there a setting on QMainWindow/QWidget to make it continue to receive update request events? Or is there some better way to implement the animation?
Edit: I'm on Mac OS X.
This may be a Qt bug. I'll investigate.
Alas, you're way overcomplicating your code.
The postEvent should be simply replaced by this->update(). Behind the scenes it posts the event for you.
One can simply connect a QTimer instance's signal to widget, SLOT(update()). If you want to save on a QObject instance, use QBasicTimer and reimplement timerEvent as follows: void MyWidget::timerEvent(QTimerEvent* ev) { if (ev.timerId() == m_timer.timerId()) update(); }
There's no need to deal with event() reimplementation. Simply reimplement paintEvent() - that's what it's for.
Qt GUI updates are performing on MainThread. So slow gui response is reasonable, if you have many gui functionality does at same time. So generally, do not overload MaiThread with so many heavey function calls.
Probable solution to speed up your GUI response.
If PostEvent is called by your MainThread( if you are using timer from main gui thread ), instead move those to backend functionality in
a worker thread and postEvent once it has been done.
you call QCoreApplication::processEvents(), after your render(); function in MainThread.
This will help system to process all the other events that are in the event-loop before to continue
Please check, following link How to improve GUI response
Note: When creating and triggering the timer it will run within your thread by default, it wont start another thread.
Since I haven't heard any more from Kuba Ober about the possibility of this being a Qt bug, I went ahead and filed a bug report: https://bugreports.qt-project.org/browse/QTBUG-33382
I was able to partially work around the problem by calling the render() function more directly — that is, instead of sending an event, receiving the event, and having the event handler call the function. I accomplished this with a dispatch queue (but not the main dispatch queue, since that's tied to the default run loop so it has the same problem). However, working with the QGLWidget on multiple threads was difficult. After trying for a while to use the moveToThread() function to make this work, and considering other factors involved in the project, I decided to use something other than Qt to display this window.