Russian Qt Forum
Ноябрь 22, 2024, 19:15 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Зависание ui при обработке нажатия кнопки. Многопоточность.  (Прочитано 1954 раз)
Barik
Новичок

Offline Offline

Сообщений: 1


Просмотр профиля
« : Декабрь 21, 2020, 17:08 »

Добрый вечер, я начал работать с qt недавно по решению нашей команды и не совсем могу понять как мне решить данную проблему.
У меня есть некоторый игровой клиент, в котором после введения имени игрока и нажатия на кнопку старта, происходит подключение к socket серверу и получения с него данных (информация о игроке, карта и т.д.).

Код:
void MainWindow::on_startButton_clicked()
{
    if (ui->userNameForm->toPlainText() != "") {
        ui->startMenu->hide();
        ui->graphview->show();

        qDebug() << "clicked";

        game_ = new Game(); // создание новой игры

        QString userName = ui->userNameForm->toPlainText(); // чтение ника с формы

        game_->connectToServer();

        game_->login(userName); // отправка имени пользователя и создание game_->player()

        game_->getMap();  // получение карты с сервера (3 слоя)

        game_->makeMap(); // формирование карты из 3 слоёв в 1 -> game_->map()

        this->setMap(game_->map()); // отрисовка в ui
        this->update();

       // отображение инфы о пользователе, полученной с сервера
        ui->userName->setText("Name: " + game_->player().name());
        ui->userTown->setText("City: " + game_->player().town().name());
        ui->userPopulation->setText("Population: " +
                                    QString::number(game_->player().town().population()) +
                                    " / " +
                                    QString::number(game_->player().town().populationCapacity()));
        ui->userProducts->setText("Products: " +
                                  QString::number(game_->player().town().product()) +
                                  " / " +
                                  QString::number(game_->player().town().productCapacity()));

        ui->userSomething->setText("Rating: " + QString::number(game_->player().rating()));

        game_->gameCycle(); // логика игры (много обращений к серверу и различных махинаций с game_.player())
    }
}

Моя проблема в том, что при выполнении game_->gameCycle(); UI зависает и не отображает карту, до того момента, как gameCylce() закончится, я понимаю, что проблема в том, что все эти функции выполняются в одном потоке, но как мне лучше решить эту проблему? Пробовал в многопоточность, но проблема в том, что я не понимаю что из этого мне надо выполнять в другом потоке. Пробовал все методы game_ выполнять в другом потоке ( как тут https://stackoverflow.com/questions/14051276/how-to-implement-frequent-start-stop-of-a-thread-qthread), но, тогда отображение информации о пользователе выполняется быстрее, чем получение информации с сервера и это крашит клиент.
В общем не знаю что тут делать вообще... И может кто подскажет как лучше организовать структуру проекта, чтобы я мог обновлять состояние карты параллельно с логикой игры
Все предложения по улучшению кода приветствуются

Как я пробовал делать:

MainWindow.h
Код:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "Map.h"
#include "Game.h"
#include "QThread"
#include "Worker.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow {
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    void setMap(std::shared_ptr<Map> m);
    void setGame(Game *game) { game_ = game; };

    Game& game() { return *game_; };
signals:
    void startWork(Game *game);
    void stopWork();

private slots:
        void on_startButton_clicked();
        void on_logoutButton_clicked();

private:
        Ui::MainWindow *ui;
        Game *game_;
        Worker *worker_;
        QThread *gameThread_;
};

#endif // MAINWINDOW_H
MainWindow.cpp
Код:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);
    ui->graphview->hide();
    ui->postInfo->hide();

    gameThread_ = new QThread;
    worker_ = new Worker;
    worker_->moveToThread(gameThread_);

    connect(this, SIGNAL(startWork(Game*)), worker_, SLOT(StartWork(Game*)));
    connect(this, SIGNAL(stopWork()), worker_, SLOT(StopWork()));

//    QTimer *timer = new QTimer(this);

//    connect(timer, SIGNAL(timeout()), this, SLOT(on_startButton_clicked()));
}

MainWindow::~MainWindow() {
    delete ui;
}

void MainWindow::setMap(std::shared_ptr<Map> m) {
    ui->graphview->setMap(m, game_->player());
}

void MainWindow::on_startButton_clicked()
{
    if (ui->userNameForm->toPlainText() != "") {
        ui->startMenu->hide();
        ui->graphview->show();

        qDebug() << "clicked";

        game_ = new Game();

        emit startWork(game_);
        gameThread_->start();

        QString userName = ui->userNameForm->toPlainText();

//        game_->connectToServer();

//        game_->login(userName);

//        game_->getMap();

//        game_->makeMap();

//        Thread *thread = new Thread(this, game_->map());
//        thread->start();

        this->setMap(game_->map());
        this->update();

        ui->userName->setText("Name: " + game_->player().name());
        ui->userTown->setText("City: " + game_->player().town().name());
        ui->userPopulation->setText("Population: " +
                                    QString::number(game_->player().town().population()) +
                                    " / " +
                                    QString::number(game_->player().town().populationCapacity()));
        ui->userProducts->setText("Products: " +
                                  QString::number(game_->player().town().product()) +
                                  " / " +
                                  QString::number(game_->player().town().productCapacity()));

        ui->userSomething->setText("Rating: " + QString::number(game_->player().rating()));

//        qDebug() << game_->map()->graph().idx();
//        qDebug() << game_->map()->trains()[0].waysLength();
//        qDebug() << game_->map()->graph().idx().at(game_->player().town().pointIdx());
//        game_->gameCycle();
    }
}

void MainWindow::on_logoutButton_clicked()
{
    emit stopWork();
//    game_->disconnect();
    ui->startMenu->show();
    ui->graphview->hide();
}

Worker.h
Код:
#ifndef WORKER_H
#define WORKER_H

#include <QObject>
//#include "Socket.h"
#include "Game.h"

class Worker : public QObject
{
    Q_OBJECT
public:
    explicit Worker(QObject *parent = nullptr);

signals:
    void SignalToObj_mainThreadGUI();
    void running();
    void stopped();

public slots:
    void StopWork();
    void StartWork(Game * game);

private slots:
    void doWork();

private:
    volatile bool isStopped, isRunning;
//    Socket *socket_;
    Game *game_;
};

#endif // WORKER_H

Worket.cpp
Код:
Worker::Worker(QObject *parent) : QObject(parent), isStopped(false), isRunning(false)
{}

void Worker::doWork() {
    if (!isRunning || isStopped) {
        return;
    }

    game_->connectToServer();

    game_->login("Sanya");
    game_->getMap();
    game_->makeMap();

    qDebug() << "Worker started to work";
    emit SignalToObj_mainThreadGUI();
//    QMetaObject::invokeMethod(this, "doWork", Qt::QueuedConnection);
}

void Worker::StopWork() {
      qDebug() << "Stop";
    isStopped = true;
    isRunning = false;
    emit stopped();
}

void Worker::StartWork(Game *game) {
    game_ = game;
    qDebug() << "Start";
    isStopped = false;
    isRunning = true;
    emit running();
    doWork();
}


Game.cpp
Код:
 
void Game::connectToServer() {
    qDebug() << "Connecting";

    socket_ = new Socket();
    this->socket_->connect();
}

void Game::login(QString userName) {
    qDebug() << "LogIn";

    QJsonObject data;
    data["name"] = userName;
    this->socket_->sendData(Request(Action::LOGIN, data));
    QJsonObject userData = socket_->getData();
    qDebug() << userData;

    player_ = new Player(userData);
}

void Game::logout() {
    qDebug() << "LogOut";

    this->socket_->close();
}

void Game::disconnect() {
    qDebug() << "Connection closed";

    this->socket_->close();
}

void Game::tick() {
    this->socket_->sendData(Request(Action::TURN, QJsonObject()));
    socket_->getData();
}

void Game::getMap() {
    socket_->sendData(Request(Action::MAP, QJsonObject({{"layer", 0}})));
     layer_0 = socket_->getData();

    socket_->sendData(Request(Action::MAP, QJsonObject({{"layer", 1}})));
     layer_1 = socket_->getData();

    socket_->sendData(Request(Action::MAP, QJsonObject({{"layer", 10}})));
     layer_2 = socket_->getData();
}

void Game::makeMap() {
    qDebug() << "Draw map";
    map_ = std::make_shared<Map>(layer_0, layer_1, layer_2, *player_);
    map_->makeWays();
}
« Последнее редактирование: Декабрь 21, 2020, 17:17 от Barik » Записан
ecspertiza
Супер
******
Offline Offline

Сообщений: 1053


С уважением, мастер конфетного цеха!


Просмотр профиля
« Ответ #1 : Декабрь 25, 2020, 18:09 »

У вас прямой вызов gameCycle, он обрабатывается в основном потоке. Нужно более корректно реализовать запуск потока. И при старте треда начать обработку gameCycle.
Записан
Kinley11
Новичок

Offline Offline

Сообщений: 6


Просмотр профиля
« Ответ #2 : Декабрь 26, 2020, 15:24 »

Не увидел в вашей новой реализации вызова GetCycle, точнее этот вызов у вас закоменчен.
А так не вызывайте значит отрисовку, до того момента, пока не получите сигнал о том, что GetCycle в другом потоке закончил свое выполнение.

Я смотрю вы вроде и сигнал в воркере добавили, только ни с чем его не связали. Также вы не должны вызывать тот же экземпляр Game, который в потоке крутится. Т.е. запустили в потоке выполняться Game, выполнился он, забились в нем нужные вам структуры - возвращаете его сигналом в MainWindow и уже только сейчас можете взять что либо из Game.
 
« Последнее редактирование: Декабрь 26, 2020, 16:03 от Kinley11 » Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.074 секунд. Запросов: 23.