QT wątki

0

Cześć,
Utworzyłem program który co jakiś czas wysyła na serwer pewne informacje.
Dane na serwerze są przetwarzane i serwer odpowiada, że jest ok albo nie ok.
Jak dane wracają do mojej aplikacji wywoływana jest metoda:
connect(networkManager,SIGNAL(finished(QNetworkReply*)),this,SLOT(updateWwwOnFinish(QNetworkReply*)));
metoda updateWwwOnFinish zapisuje w bazie czas przyjścia odpowiedzi i wyświetla QMessageBox że jest ok lub nie ok.

Mój problem polega na tym, że czasem odpowiedź jest po kilku sekundach i jeśli aktualnie program wykonuję jakąś czynność to zawiesza cały komputer.
Np. mam w programie tabele z uruchomioną opcją drag and drop, jeśli złapie komórkę i zacznę ją przeciągać i w tym momencie przyjdzie odpowiedź to program się zawiesza.
Ten problem występuje tylko jeśli chce wyświetlić QMessageBox z informacją czy dane zostały poprawnie zapisane na serwerze, jeśli wyłączę pokazywanie QMessageBox cały kod wykonuje się bez problemu i nic się nie zawiesza nawet jeśli aktualnie używam drag and drop.

Chciałbym was zapytać czy w takiej sytuacji może pomóc użycie wątków? (nigdy ich nie używałem)
A może jest lepszy sposób aby sprawdzić czy program jest zajęty i ewentualnie nie pokazywać QMessageBox?

1

błąd masz w linii 154 tam gdzie używasz Sleep i nieskończonej pętli.
Moja kryształowa kula nic więcej nie pokazuje.

0

Może pomóc użycie wątków (czekasz aż zakończy się wątek odpowiedzialny za pobieranie danych z serwera).
Ewentualnie możesz użyć też connect i nadać sygnał po zakończeniu pobierania danych z serwera.

p.s. Nie używaj funkcji sleep, ani żadnej funkcji blokującej program.

0

Ok, faktycznie lepiej będzie jak dam konkretny przykład.
Utworzyłem nowy projekt w QTCreatorze.
Na formie umieściłem tylko QTableWidget oraz przycisk.
W ustawianiach QTableWidget ustawiłem 10 kolumn i 10 wierszy oraz kliknąłem możliwość drag and drop.
Pod przyciskiem mam taki kod:

void MainWindow::on_wyslijNaSerwer_clicked()
{
    QString serverUrl = "http://127.0.0.1:8000/zaktualizuj";

    QByteArray postArray;

    QNetworkAccessManager *networkManager = new QNetworkAccessManager(this);

    connect(networkManager,SIGNAL(finished(QNetworkReply*)),this,SLOT(updateWwwOnFinish(QNetworkReply*)));

    QUrl params;

    QUrlQuery query;

    QString aktual = "to wysylamyyyyy";

    query.addQueryItem("aktual" , aktual);
    params.setQuery(query);

    postArray = params.toEncoded(QUrl::RemoveFragment);
    QNetworkRequest networkRequest(serverUrl);
    networkRequest.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded");
    networkManager->post(networkRequest,postArray);
}

Tak wygląda metoda która wywoływana jest kiedy z serwera przychodzi odpowiedź:

void MainWindow::updateWwwOnFinish(QNetworkReply *rep)
{
    QMessageBox msgBox;

    QByteArray bts = rep->readAll();
    QString str(bts);
qDebug() << " str " << str;
qDebug() << " rep->error() " << rep->error();

        msgBox.setWindowFlags(Qt::WindowStaysOnTopHint);
        msgBox.setWindowTitle("Jest ok");
        msgBox.setText("Dane zostały zapisane.");
        msgBox.exec();
}

Na serwerze jest tak:

public function AktualizujAction(Request $request)
    {
        sleep(5);
        return new Response(
            '0'
        );
    }

Żeby była jasność skrypt na produkcyjnym serwerze tak nie wygląda, zrobiłem go teraz tylko dlatego aby odpowiedź była po kilku sekundach jak to się czasem zdarza kiedy korzystam z bardzo wolnego internetu mobilnego i są problemy z zasięgiem albo serwer jest obciążony.

Teraz uruchamiam program, klikam przycisk który wywołuje metodę on_wyslijNaSerwer_clicked(), łapę jedną komórkę i przeciągam po tabeli ale nie puszczam. Po kilku sekundach przychodzi odwiedź z serwera, wykonuje się metoda updateWwwOnFinish() pojawia się msgBox i komputer jest zawieszony :/
Jednak kiedy usunę msgBox.exec() z kodu cała metoda updateWwwOnFinish() wykonuje się i program działa bez problemu

0

Joł.
exec() wykonany na messageBoxie nie jest w tym przypadku najlepszym pomysłem. Poczytaj sobie co robi exec() http://doc.qt.io/qt-5/qmessagebox.html#exec i http://doc.qt.io/qt-5/qdialog.html#exec . Wywołanie exec() m. in. blokuje Ci wątek GUI. Dlatego obsługa zdarzeń w MainWindow (i innych widgetach z wyjątkiem MessageBoxa) przestaje działać.
Zamiast exec() użyj show(). Zrób msgBoxa polem klasy i wyłącz mu modalność. Powinno pomóc.
Ale że wiesza komputer? Nawet e messageBoxa nie mogłeś kliknąć?

0

Tak całkowicie zawiesza mi Ubuntu :/
Mógłbyś podpowiedzieć jak zrobić msgBoxa polem klasy bo nie bardzo wiem o co chodzi

zrobiłem coś takiego ale teraz msgbox się nie pojawia.

msgBox.setModal(Qt::NonModal);
msgBox.setWindowTitle("Błąd połączenia");
msgBox.setText(trUtf8("Brak połączenia ze stroną www. Sprawdź połączenie z internetem."));
msgBox.show();
1

No właśnie. Twój messageBox jest tworzony w updateWwwOnFinish i istnieje tylko w niej. show() w odróżnieniu od exec() nie blokuje wątku i dlatego zaraz po wykonaniu show(), które jest ostatnią instrukcją updateWwwOnFinish się kończy i obiekt messageBox przestaje istnieć. Jeśli messageBox byłby składnikiem klasy, to istniał by tak długo, jak długo istniałby obiekt mainWindow. Czyli wystarczająco długo.

Ale możesz zrobić to nawet prościej. W updateWwwOnFinish twórz messageBoxa dynamicznie (używając new). Ustaw mu MainWindow jako parenta i ustaw atrybut setAttribute(Qt::WA_DeleteOnClose, true). Dzięki temu za każdym wykonaniem updateWwwOnFinish będzie tworzony nowy messageBox. A przy zamykaniu będzie niszczony. A jeśli nie chcesz, żeby wyskakiwało dużo messageBoxów, tylko chcesz, żeby był jeden, to po prostu w klasie MainWindow dodaj pole typu QMessageBox i to będzie Twój jedyny messageBox, i będziesz mógł go pokazywać w updateWwwOnFinish.

0

Dzięki za odpowiedź, miałeś rację, taki kod rozwiązuje problem:

QMessageBox *msgBox = new QMessageBox();
msgBox->setWindowFlags(Qt::WindowStaysOnTopHint);
msgBox->setModal(Qt::NonModal);
msgBox->setAttribute(Qt::WA_DeleteOnClose, true);
msgBox->setWindowTitle("Błąd połączenia");
msgBox->setText(trUtf8("Brak połączenia ze stroną www. Sprawdź połączenie z internetem."));
msgBox->show();
1

No tak nie do końca. Teraz każdy nowy messageBox to kolejny wyciek pamięci. Możesz tego uniknąć dodając setAttribute(Qt::WA_DeleteOnClose, true). Ustawiaj parenta messageBoxom na MainWIndow, wtedy podczas niszczenia MainWindow zostaną też zniszczone niezamknięte messageBoxy. Jakiego typu argument przyjmuje setModal() (czepia się ;))?

ecex wtedy, kiedy chcesz zmusić usera do reakcji (np. zmusić go do odpowiedzi na pytanie - wątek będzie zablokowany dopóki user nie odpowie). poza tym exec() zwraca wartość, show() nie,

a co do ego wieszania, to nie wiem...

Nara

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