1

I have a QDialog on my main thread and I have some logic that happens on a separate thread. When the logic begins, a signal is emitted connected to show() on the dialog. When the logic ends, a signal is emitted that is connected to hide() on the dialog. When the logic actually does work, the dialog is show/hide properly. If the logic does "nothing" and the signals are just emitted sequentially, the dialog doesn't always show/hide properly.

My connections are made similar to this:

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget* parent = 0) :
        Ui(new Ui::MainWindowUi),
        Transferer(new DataTransferer()),
        TransferProgress(this),
        TransferThread()
    {
        Ui->setupUi();
        connect(&Transferer, SIGNAL(Begin()), &TransferProgress, SLOT(show()));
        connect(&Transferer, SIGNAL(End()), &TransferProgress, SLOT(hide()));

        Transferer.moveToThread(&TransferThread);
        TransferThread.start();

        Transferer.DoStuff(true);
    }

    virtual ~MainWindow()
    {
        TransferThread.quit();
        TransferThread.wait(1000);            

        delete Ui;
        Ui = NULL;
    }

private:
    Ui::MainWindowUi* Ui;
    DataTransferer Transferer;
    TransferProgressDialog TransferProgress;
    QThread TransferThread;
}

The logic looks similar to this:

class DataTransferer : public QObject
{
    Q_OBJECT
public:
    DataTransferer(QObject *parent) : QObject(parent) {}
    virtual ~DataTransferer() {}

    void DoStuff(bool dontDoStuff)
    {
        emit Start();
        if (!dontDoStuff)
        {
            QThread::sleep(1);
        }
        emit End();
    }
}

When the DataTransferer does stuff, everything works fine. When the dialog is shown and hidden in rapid succession, I get the ghost dialog approximately every other time I call DoStuff().

I used QThread::currentThreadId() and verified that the dialog and logic are running on separate threads.

Why would my dialog not hide properly in this case? Should I just force my logic to always run for at least a few hundred milliseconds (that solution is bad)? Is there a way I can have my dialog make sure it's fully loaded before trying to hide itself? Should I handle these signals/slots differently?

EDIT: I've currently resigned to just putting a QThread::sleep(1) after I emit the signal to show() the dialog. I don't like this solution, but nothing else has seemed to work. The sleep(1) allows the dialog to come all the way up before hiding it. I was also able to get this to work with QThread::msleep(10), but that still resulted in the ghost dialog about 1 in 6 tries.

I tried using a member QMutex in the dialog logic whenever I called either show() or hide(), but this didn't work.

I changed all cross-thread connections to use Qt::BlockingQueuedConnection and Qt::QueuedConnection and neither attempt was successful.

I tried moving the slot connections from the dialog to the object that sets up the connections and then calling the slots directly, but that didn't prove successful either.

Larry Price
  • 429
  • 1
  • 4
  • 15

3 Answers3

2

I have the same issue, show dialog and when get some signals to close it, when time less than 20ms(which means quickly hide the dialog), it will left a ghost dialog.

So, i just use

QTimer::singleShot(50, this, [this](){
    hide(); //hide dialog
});

in close handler function. It seems work well.

Shun
  • 147
  • 11
  • 50ms was not enough in my case. I had to use an even bigger timeout (I use 2000ms which is OK for me but probably much lower values are possible as well) – Marc Van Daele Sep 05 '19 at 10:00
0

My guess is that the problem occurs because "show" and "hide" calls interlace. To verify this, use a semaphore - lock the object until show have finished, and wait on it in hide. Also look at the top-voted answer here for another possible (perhaps better) solution: connecting signal/slot across different threads between QObjects

Community
  • 1
  • 1
BartoszKP
  • 34,786
  • 15
  • 102
  • 130
  • Used a QMutex to set a lock in both Show()/Hide() functions to lock/unlock before and after the actions; no success. I've also read through the linked solution and attempted all logical connection types and the issue still persists. – Larry Price Aug 04 '13 at 02:50
0

Use Qt::BlockingQueuedConnection to connect signals to slots. Be sure that event loop of main thread is not blocked. Also, if your worker thread uses a lot of cpu time - you may call QThread::yeldCurrentThread() call.

Dmitry Sazonov
  • 8,801
  • 1
  • 35
  • 61