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