0

I have an existing multithreaded application I'm trying to slap a very simple QT GUI onto the front end of.

In particular, I've tried to implement a very simple log using a QPlainTextEdit object. Whenever one of these OS threads kicks off a certain action, I'm putting some diagnostic info in the widget with a call to its appendPlainText() method.

Of course my first crack at this had synchronization issues, so I created a class wrapping the QPlainEdit with a mutex to synchronize things. Essentially this:

class synced_output {
public:
    std::mutex mutex;
    QPlainTextEdit & text;

    synced_output (QPlainTextEdit * text_output) : text(*text_output) {}
    void put_line (const std::string & line) {
        std::lock_guard<std::mutex> lock(mutex);

        text.appendPlainText(line.c_str());
    }
private:
    synced_output operator= (synced_output &){}
};

That seems to have fixed the race condition nicely. However, the contents of the PlainTextEdit widget do not visibly update until I go interact with the window in some way (merely clicking it suffices). If it adds enough text, I can see the scrollbar come up and move around, but no text until I click.

Based on some advice I saw online, I tried putting a text.repaint() in there, but that just causes a crash complaining "Cannot send events to objects owned by a different thread". Which makes sense I suppose, but I still need to do so. So how can I get this to display properly after a change? Is there some other better idiom for handling QT calls to change widget contents from multiple OS threads?

T.E.D.
  • 44,016
  • 10
  • 73
  • 134
  • Look at the answer [here](http://stackoverflow.com/questions/3297456/invoke-slot-method-without-connection). With queued connection there will be no need for your own synchronisation either. – Rostislav Nov 10 '16 at 23:21
  • 1
    Or just make a signal and connect it to the `appendPlainText` slot with a `Qt::QueuedConnection` (see [here](http://doc.qt.io/qt-5/threads-qobject.html#signals-and-slots-across-threads)) which is probably the cleanest way. – Rostislav Nov 10 '16 at 23:26
  • @Rostislav - The former looks promising (although my compiler currently has issues with the Q_ARG formulation it looks like. I'll have to bang on it when I get back in tomorrow). I looked at doing the latter earlier, but it looks like I'd have to create some kind of bogus QT object with its own event loop just for this purpose, no? – T.E.D. Nov 10 '16 at 23:31
  • Even if you are serializing accesses to GUI functions, you can't guarantee that GUI events don't arrive to the main thread while you are accessing the component from your thread. All GUI related classes in Qt are meant to run only from the GUI thread. see [here](https://doc.qt.io/qt-5/thread-basics.html#gui-thread-and-worker-thread). You need to emit cross thread signals, to invoke slots that update your GUI in the main thread (as other comments suggested). – Mike Nov 10 '16 at 23:34
  • @T.E.D. Yes, you would need a `QObject` to make a signal, but it won't create a message _loop_. The queued signal should just post a message to the GUI thread which will eventually pick that message up. I think that if you look at the moc'ed cpp of that object, it will likely do the same `invokeMethod` under the covers. – Rostislav Nov 10 '16 at 23:48
  • @Rostislav - Your first suggestion seems to be working. One line of code, easy peasy. Put all this in an answer and I'll accept it. – T.E.D. Nov 11 '16 at 16:58

0 Answers0