Моя проблема в том, что я нашел способ переключать интерфейсы. Но когда он переключает пользовательский интерфейс, .cpp пользовательского интерфейса не загружается.
mainmenu.cpp
#include "mainmenu.h"#include "ui_mainmenu.h"
MainMenu::MainMenu(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainMenu),
newgame(new Ui::PlayerMenu),
optionmenu(new Ui::OptionMenu)
{
ui->setupUi(this);
QPixmap background("../../res/Testbg.png");
background = background.scaled(this->size(), Qt::IgnoreAspectRatio);
QPalette palette;
palette.setBrush(QPalette::Background, background);
this->setPalette(palette);
}
MainMenu::~MainMenu()
{
delete ui;
}
void MainMenu::on_pushButtonNewGame_clicked()
{
changeAppearance(1);
}
void MainMenu::on_pushButtonOption_clicked()
{
changeAppearance(2);
}
void MainMenu::changeAppearance(int id)
{
if(id == 0)
{
ui->setupUi(this);
}
else if(id == 1)
{
newgame->setupUi(this);
}
else if(id ==2)
optionmenu->setupUi(this);
}
mainmenu.h
#ifndef MAINMENU_H
#define MAINMENU_H
#include <QMainWindow>
#include "playermenu.h"#include "optionmenu.h"
namespace Ui {
class MainMenu;
}
class MainMenu : public QMainWindow
{
Q_OBJECT
public:
explicit MainMenu(QWidget *parent = 0);
~MainMenu();private slots:
void on_pushButtonNewGame_clicked();
void on_pushButtonOption_clicked();
private:
void changeAppearance(int id);Ui::MainMenu *ui;
Ui::PlayerMenu *newgame;
Ui::OptionMenu *optionmenu;
};
#endif // MAINMENU_H
playermenu.cpp
#include "playermenu.h"#include "ui_playermenu.h"
PlayerMenu::PlayerMenu(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::PlayerMenu),
levelmenu(new Ui::LevelMenu)
{
ui->setupUi(this);
QPixmap background("../../res/Testbg.png");
background = background.scaled(this->size(),Qt::IgnoreAspectRatio);
QPalette palette;
palette.setBrush(QPalette::Background, background);
this->setPalette(palette);
}
...
playermenu.h
#ifndef PLAYERMENU_H
#define PLAYERMENU_H
#include <QMainWindow>
#include <ui_playermenu.h>
#include "levelmenu.h"
namespace Ui {
class PlayerMenu;
}
class PlayerMenu : public QMainWindow, Ui::PlayerMenu
{
Q_OBJECT
public:
explicit PlayerMenu(QWidget *parent = 0);
~PlayerMenu();
...
private:
Ui::PlayerMenu *ui;
Ui::LevelMenu *levelmenu;
};
#endif // PLAYERMENU_H
Я новичок в QT, так что я действительно не знаю, является ли это правильным способом сделать это.
У кого-нибудь есть подсказка, где проблема или есть обходной путь?
Похоже, вы хотите иметь одно окно, которое переключается между различными состояниями. Я бы не рекомендовал использовать для этого несколько файлов .ui. Возможно, есть несколько способов:
Использовать QStackedWidget — Вы можете добавить это в дизайнере пользовательского интерфейса, думая об этом как о наборе страниц, которые вы выбираете программно. Используйте это, и ваши кнопки поменяют его на соответствующую страницу.
Иметь несколько разных классов для ваших разных взглядов, и установить центральный виджет главного окна для разных виджетов, когда это необходимо.
Лично я бы пошел на вариант 1.
Занятия в Ui
пространство имен генерируется uic
и предназначены для построения иерархии виджетов на пустом виджете без макета. Пытаясь выполнить обмен, вы используете этот код, чтобы делать то, для чего он никогда не предназначался.
Чтобы ваш подход работал, вам нужно сначала избавиться от всех объектов, установленных предыдущим setupUi
вызов. Это выполнимо, но проблематично — поскольку нет простого способа перечислить все объекты в ui
Сама структура, вам придется прибегнуть к итерации выложенных потомков вашего виджета — и тогда нет общего способа узнать, происходят ли эти потомки из вашего другого кода или из сгенерированного кода.
Кроме того, все пользовательские интерфейсы должны быть разработаны поверх QMainWindow
, Замена интерфейса на основе другого типа виджета не будет работать, так как базовый виджет является QMainWindow
что нужно centralWidget
, Это взлом, но это работает:
// https://github.com/KubaO/stackoverflown/tree/master/questions/ui-swap-42416275
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
// mock uic output
namespace Ui {
struct MainMenu { void setupUi(QMainWindow*) {} };
struct PlayerMenu { void setupUi(QMainWindow*) {} };
struct OptionMenu { void setupUi(QMainWindow*) {} };
}
class MainMenuHack : public QMainWindow {
Q_OBJECT
enum class UiKind { MainMenu, PlayerMenu, OptionMenu };
Ui::MainMenu uiMainMenu;
Ui::PlayerMenu uiPlayerMenu;
Ui::OptionMenu uiOptionMenu;
void clearLayout(QLayout * layout) {
if (!layout) return;
while (layout->count()) {
QScopedPointer<QLayoutItem> item{layout->takeAt(0)};
if (!item)
continue;
delete item->widget();
clearLayout(item->layout());
}
}
public:
MainMenuHack(QWidget * parent = {}, Qt::WindowFlags flags = {}) :
QMainWindow{parent, flags}
{
setAppearance(UiKind::MainMenu);
}
void setAppearance(UiKind kind) {
clearLayout(layout());
switch (kind) {
case UiKind::MainMenu: return uiMainMenu.setupUi(this);
case UiKind::PlayerMenu: return uiPlayerMenu.setupUi(this);
case UiKind::OptionMenu: return uiOptionMenu.setupUi(this);
}
}
};
Заметки:
1. Держите ui
объекты по значению, а не по указателю. Дополнительная косвенность бесполезна.
2. Используйте строго типизированные перечисления вместо магических констант для обозначения выбора.
Увы, нам не нужно прибегать к взломам. Мы можем использовать QStackedWidget
поменять местами видимые панели.
Во-первых, давайте сделаем UiWidget
класс, который оборачивает данный Ui::
Тип и соответствующий виджет. Он автоматически устанавливает дочерние элементы в виджете, добавляет себя в составной родительский элемент виджета и имеет помощника, который устанавливает его в качестве текущего виджета в стеке.
template <typename Ui>
struct ui_traits : ui_traits<decltype(&Ui::setupUi)> {};
template <typename Ui, typename Widget>
struct ui_traits<void(Ui::*)(Widget*)> {
using widget_type = Widget;
};
template <typename Ui, typename Widget = typename ui_traits<Ui>::widget_type>
struct UiWidget : Widget, Ui {
UiWidget(QWidget * parent = {}) : Widget{parent} { this->setupUi(this); }
UiWidget(QStackedWidget * parent) : UiWidget{static_cast<QWidget*>(parent)} {
parent->addWidget(this);
}
void setCurrent() {
auto stack = qobject_cast<QStackedWidget*>(this->parent());
if (stack) stack->setCurrentWidget(this);
}
};
Теперь каждый Ui
класс может быть основан на другом типе виджета, например Ui::MainMenu
может быть основан на QMainWindow
но например Ui::OptionMenu
может быть основан на QDialog
,
MainMenu
теперь может быть просто QStackedWidget
который содержит подвиджеты с их структурами пользовательского интерфейса:
class MainMenu : public QStackedWidget {
Q_OBJECT
enum class UiKind { MainMenu, PlayerMenu, OptionMenu };
UiWidget<Ui::MainMenu> uiMainMenu{this};
UiWidget<Ui::PlayerMenu> uiPlayerMenu{this};
UiWidget<Ui::OptionMenu> uiOptionMenu{this};
public:
MainMenu(QWidget * parent = {}, Qt::WindowFlags flags = {}) :
QStackedWidget{parent}
{
setWindowFlags(flags);
setAppearance(UiKind::MainMenu);
}
void setAppearance(UiKind kind) {
switch (kind) {
case UiKind::MainMenu: return uiMainMenu.setCurrent();
case UiKind::PlayerMenu: return uiPlayerMenu.setCurrent();
case UiKind::OptionMenu: return uiOptionMenu.setCurrent();
}
}
};
В обоих MainMenu
а также MainMenuHack
, uiFoo
члены-это их соответствующие Ui::Class
например, uiMainMenu
это Ui::MainMenu
,
На данный момент, если setAppearance
может быть закрытым методом, вам не нужно никакого косвенного обращения и вы можете оперировать uiFoo
Участники напрямую: заменить любой setAppearance(UiKind::Foo)
с uiFoo.setCurrent()
:
class MainMenu : public QStackedWidget {
Q_OBJECT
UiWidget<Ui::MainMenu> uiMainMenu{this};
UiWidget<Ui::PlayerMenu> uiPlayerMenu{this};
UiWidget<Ui::OptionMenu> uiOptionMenu{this};
public:
MainMenu(QWidget * parent = {}, Qt::WindowFlags flags = {}) :
QStackedWidget{parent}
{
setWindowFlags(flags);
uiMainMenu.setCurrent();
}
};
Код работает как в Qt 5, так и в Qt 4.