Jak uruchomić zwykły wątek w kontekście głównego wątku Qt?

0

Czy watek std::thread może uruchomić funkcję w kontekście głównego wątku aplikacji Qt i zaczekać na zakończenie tej funkcji ?
Coś jak Qt::BlockingQueuedConnection ? Tyko nie mogę tego uzyć jak oba watki nie sa z Qt

main.cpp

int main(int argc, char *argv[])
{
    using namespace httplib;

    QApplication a(argc, argv);
    Widget w;
    w.show();

    Server svr;

    svr.Get("/img", [&](const httplib::Request &, httplib::Response &res) {
        QByteArray data;
        if (w.getPng(data))
        {
            res.set_content(data.data(), data.length(), "image/png");
        }
        else
        {
            res.status = 502;

        }
    });

    auto httpThread = std::thread([&]() { svr.listen("0.0.0.0", 8181); });

    system("start http://localhost:8181/img");

    return a.exec();
}

getPng musi byc uruchomione w głównym wątku

  bool Widget::getPng(QByteArray &data)
  {
      auto pixmap = grab();
      QImage image = pixmap.toImage();

      QBuffer inBuffer(&data);
      inBuffer.open(QIODevice::WriteOnly);
      image.save(&inBuffer, "PNG");
      return true;
  }

jak to pokonać ?

3

gui nie powinno się odpalać po za głównym wątkiem.
https://doc.qt.io/qt-6/thread-basics.html
As mentioned, each program has one thread when it is started. This thread is called the "main thread" (also known as the "GUI thread" in Qt applications). The Qt GUI must run in this thread. All widgets and several related classes, for example QPixmap, don't work in secondary threads. A secondary thread is commonly referred to as a "worker thread" because it is used to offload processing work from the main thread.
edit:
na szybko z głowy sama pixmapa i konwersja do qimage zrobić w wątku głównym(albo trzymać w cache ale wiadomo to może być duże). I dopiero odpalić tą konwersje w innym wątku jako lambdę nawet od bólu a najlepiej funkcję po za widgetem.

0

zrobiłem taki eksperyment nie wierząc ze zadziała:

class MainWindow : public QWidget
signals:
    void signal_getPng();
private slots:
    void slot_getPng()

...
MainWindow::MainWindow()
{
  connect(this, &MainWindow::signal_getPng, this, &MainWindow::slot_getPng, Qt::BlockingQueuedConnection);

i o dziwo emit signal_getPng w kontekście wątku nie Qt zachowuje się zgodnie z oczekiwaniami , slot_getPng wykonuje sie w kontekście głównego wątku a po skończeniu watek nie Qt konturuje pracę

0

Rozwiązanie ze slotem i signalem jest fajne o ile to jest jedyny przypadek kiedy potrzebujesz zrobić coś na main threadzie. Jeśli będzie tego więcej to źle to się zeskaluje. Będziesz miał bardzo dużo slotów i signalów a kod będzie coraz mniej przejrzysty.

Generalny mechanizm powinien wyglądać mniej więcej tak:


template< typename T >
T dispatchToMainThreadWithResult(std::function<T()> callback)
{
    // any thread
    T result;
    std::atomic<bool> resultReady(false);
    QTimer* timer = new QTimer();
    timer->moveToThread(qApp->thread());
    timer->setSingleShot(true);
    QObject::connect(timer, &QTimer::timeout, [timer, callback, &result, &resultReady]()
    {
        // main thread
        result = callback();
        resultReady = true;
        timer->deleteLater();
    });
    QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection, Q_ARG(int, 0));
    while (resultReady.load() == false) // pewnie przydałby się lepszy mechanizm czekania
    {
        QThread::sleep(5);
    }
    return result;
}

int main(int argc, char *argv[])
{
//....
    Server svr;

    svr.Get("/img", [&](const httplib::Request &, httplib::Response &res) {

        const auto data = dispatchToMainThreadWithResult<std::optional<QByteArray>>([&] {
            QByteArray data;
            if (w.getPng(data))
            {
                return std::optional<QByteArray>(data);
            }
            else
            {
                return std::optional<QByteArray>(std::nullopt);
            }
        });

        if(data.has_value())
        {
            res.set_content(data.data(), data.length(), "image/png");
        }
        else
        {
            res.status = 502;
        }
    });
//...
}

Zaciągnięte z https://stackoverflow.com/a/54029758 i https://riptutorial.com/qt/example/21783/using-qtimer-to-run-code-on-main-thread

1 użytkowników online, w tym zalogowanych: 0, gości: 1