QT/С++ - доступ к интерфейсу MainWindow из другого класса

Я начинаю как С++, так и Qt, поэтому, возможно, это тривиально. Конечно, похоже, что это должно быть просто, но я искал ответ в течение нескольких часов и не могу найти решение. Я делаю простую настольную игру, где MainWindow ui (сделанный в QtDesigner) содержит холст для игрового поля (QGraphicsView). Теперь main.cpp так же просто, как может быть:

MainWindow Game;

int main(int argc, char *argv[])
{
 QApplication a(argc, argv);

 Game.show();

return a.exec();
}

Поскольку мне нужно получить доступ и редактировать MainWindow Widgets из другого совершенно несвязанного класса, я думал, что самый простой способ - просто сделать MainWindow глобальной переменной. Похоже, что этот подход был очень неправильным. При попытке запустить проект в QtDesigner я получаю ошибку библиотеки Microsoft Visual С++: приложение запросило runtime, чтобы завершить его необычным способом.

Итак, каков правильный способ делать то, что мне нужно?

Помимо MainWindow у меня есть диалог для новой игры (QDialog, сгенерированный из QtDesigner), который отображается после нажатия элемента меню в MainWindow. Когда пользователь вводит все параметры для игры и щелкает ОК в диалоговом окне, я создаю пользовательский класс, отличный от Qt, который называется GameState. Этот класс предназначен для управления самой игрой, рисования доски, запроса пользователя и т.д. Однако, поскольку этот класс создан в QDialog, он не знает о существовании MainWindow, поэтому я ничего не могу сделать с MainWindow из этого класса. Как я могу изменить MainWindow из несвязанного класса, затем?

Также, как работает функция setEnabled()? Кажется, он ничего не делает. Любой виджет, который я установил как отключенный в QtDesigner, а затем попытаюсь включить эту функцию, по-прежнему не работает в графическом интерфейсе...

Ответы

Ответ 1

Прежде всего, создайте MainGame плохую идею, прежде чем создать свой объект QApplication. Если вы хотите, чтобы ваш объект MainGame глобально доступен, как это, он должен быть указателем:

MainWindow *Game;
int main (int argc, char **argv)
{
  QApplication a (argc, argv);

  Game = new MainWindow();
  Game->show();

  int result = a.exec();

  delete Game;
  Game = NULL;

  return result;
}

Этот подход, однако, не самый элегантный. Есть два гораздо лучших варианта.

  • Объект QApplication фактически хранит все окна верхнего уровня, такие как ваш MainGame, что означает, что вы всегда можете его использовать через QApplication::topLevelWidgets(), который является статической функцией и возвращает список со всеми виджетами верхнего уровня. Поскольку у вас только один, первый из них - ваш MainGame. Недостатком является то, что вам придется его использовать, но использование Qts qobject_cast<MainGame*>(...) довольно безопасно. Вы должны будете проверить результат, но убедитесь, что он не является указателем NULL.

  • Используйте шаблон дизайна singelton. Вы должны сохранить глобальный указатель игры в исходном (cpp) файле самого класса Game (подкласс QMainWindow), а ваш класс Game должен реализовать статический общедоступный метод, который возвращает этот глобальный указатель. Поэтому, если любому другому классу нужен указатель Game, он просто вызывает:

    MyGame *theGame = MyGame::getInstance();
    

    например.

Относительно вашей проблемы с setEnabled(). Отправьте соответствующий код. Если он слишком много, вы можете отправить мне файл *.ui и кусок кода по почте.

С уважением, D

Ответ 2

Самый простой способ сделать это - сначала настроить сигнал в заголовочном файле вашего другого класса, чтобы сказать, выполнить функцию для управления объектом в основном классе, как этот

signals:
    void disableLoadButtton();

Затем создайте слот под частными слотами в заголовочном файле главного окна, например

private slots:
     void disableLoadButtton();

Затем создайте функцию как функцию-член в главном окне, чтобы манипулировать объектом

void MainWindow::disableLoadButton()
{
     ui->loadButton->setenabled(false);
}

Затем добавьте следующую строку в другую функцию-член главного окна, которая говорит, что устанавливает страницу. Мой другой класс называется searchWidget

void MainWindow::setUpPage()
{
    connect(searchWidget, SIGNAL(disableLoadButton()), this, SLOT(disableLoadButton()));
}

Затем все, что вам нужно сделать, чтобы отключить loadButton (который является объектом в MainWindow), заключается в добавлении следующей строки в любую функцию-член моего другого класса searchWidget

void searchWidget::performSomething()
{
      emit disableLoadButton();
}

Затем он будет манипулировать объектом loadButton в mainwindow изнутри функции-члена другого класса searchWidget.

Ответ 3

Если ваше приложение имеет только одно окно, вы можете просто использовать:

MainWindow * win = (MainWindow *) qApp::activeWindow();

Ответ 4

Я делаю так:

QMainWindow* getMainWindow()
{
    foreach (QWidget *w, qApp->topLevelWidgets())
        if (QMainWindow* mainWin = qobject_cast<QMainWindow*>(w))
            return mainWin;
    return nullptr;
}

Ответ 5

Если вам нужно получить доступ к своему MainWindow из другого окна, вы, вероятно, ошибаетесь. Использование другого класса для передачи информации с помощью сигналов/слотов, вероятно, гораздо лучше подходит

Ответ 6

В прошлом я использовал подход, описанный в этом ответе (находится в проекте Qtractor).

Теперь я использую свойство QObject 'name' и нахожу его где угодно, как описано здесь.

main.c

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

mainwindow.cpp

#include <QString>
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "c.h"

MainWindow * MainWindow::pMainWindow = nullptr;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    pMainWindow = this;
    setCentralWidget(&m_pb);
    connect(&m_pb, SIGNAL(clicked()), this, SLOT(on_pb_clicked()));
}

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

// kind of singleton reference.
MainWindow *MainWindow::getMainWinPtr()
{
    return pMainWindow;
}

void MainWindow::pbSetText()
{
    m_pb.setText(QString{"Call from c."});
}

void MainWindow::on_pb_clicked()
{
    c C;  // call of MainWindow from class c ctor
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QString>
#include <QPushButton>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

    static MainWindow * getMainWinPtr();
    void pbSetText();

public slots:
    void on_pb_clicked();

private:
    static MainWindow * pMainWindow;

    Ui::MainWindow *ui;
    QPushButton m_pb{QString{"Press me."}, this};
};

#endif // MAINWINDOW_H

c.cpp

#include "c.h"
#include "mainwindow.h"

c::c()
{
    MainWindow * mw = MainWindow::getMainWinPtr();
    mw->pbSetText();
}

c.h

#ifndef C_H
#define C_H

class c
{
public:
    explicit c();
};

#endif // C_H