QT - 切换问题。ui
QT - Problems with switching .ui
我的问题是我找到了切换 UI 的方法。但是当它切换 UI 时,UI 的 .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 的新手,所以我真的不知道这是否是正确的方法。
有没有人知道问题出在哪里或者是否有解决方法?
听起来你想要一个在不同状态之间切换的 window。我不建议使用多个 .ui 文件来执行此操作。一些更好的方法可能是:
使用 QStackedWidget - 您可以在 UI 设计器中添加它,将其视为您以编程方式 select 的一组页面。使用它并让您的按钮将其更改为适当的页面。
有多个不同的 类 用于不同的视图,set the central widget 的主要 window 用于需要时的不同小部件。
就个人而言,我会选择选项 1。
Ui
命名空间中的 classes 由 uic
生成,旨在在没有布局的空小部件上构建小部件层次结构。通过尝试交换,您正在滥用该代码来执行它从未打算执行的操作。
要使您的方法奏效,您必须首先删除之前 setupUi
调用安装的所有 objects。这是可行的,但有问题 - 因为没有简单的方法来枚举 ui
结构本身中的所有 objects,你必须求助于迭代你的 laid-out children小部件 - 然后没有通用的方法可以知道这些 children 是来自您的其他代码还是来自生成的代码。
此外,所有用户界面都必须在 QMainWindow
之上设计。换出基于另一种小部件类型的界面是行不通的,因为基础小部件是 QMainWindow
,需要 centralWidget
。这是一个 hack,但它有效:
// https://github.com/KubaO/Whosebugn/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
objects。额外的间接是无用的。
2. 使用强类型枚举而不是魔法常量来表示选择。
唉,我们不需要诉诸黑客。我们可以使用 QStackedWidget
来交换可见窗格。
首先,让我们制作一个 UiWidget
class 来包装给定的 Ui::
类型及其相应的小部件。它会自动在小部件上设置 children,将自己添加到堆叠小部件 parent,并有一个助手将其设置为堆栈中的当前小部件。
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
class 都可以基于不同的小部件类型,例如Ui::MainMenu
可以基于 QMainWindow
但例如Ui::OptionMenu
可以基于 QDialog
.
MainMenu
现在可以简单地成为一个包含 sub-widgets 及其 ui 结构的 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
成员 are-a 他们各自的 Ui::Class
,例如uiMainMenu
is-a Ui::MainMenu
.
此时,如果setAppearance
可以做成non-public方法,则不需要任何间接,可以直接对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 下均有效。
我的问题是我找到了切换 UI 的方法。但是当它切换 UI 时,UI 的 .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 的新手,所以我真的不知道这是否是正确的方法。 有没有人知道问题出在哪里或者是否有解决方法?
听起来你想要一个在不同状态之间切换的 window。我不建议使用多个 .ui 文件来执行此操作。一些更好的方法可能是:
使用 QStackedWidget - 您可以在 UI 设计器中添加它,将其视为您以编程方式 select 的一组页面。使用它并让您的按钮将其更改为适当的页面。
有多个不同的 类 用于不同的视图,set the central widget 的主要 window 用于需要时的不同小部件。
就个人而言,我会选择选项 1。
Ui
命名空间中的 classes 由 uic
生成,旨在在没有布局的空小部件上构建小部件层次结构。通过尝试交换,您正在滥用该代码来执行它从未打算执行的操作。
要使您的方法奏效,您必须首先删除之前 setupUi
调用安装的所有 objects。这是可行的,但有问题 - 因为没有简单的方法来枚举 ui
结构本身中的所有 objects,你必须求助于迭代你的 laid-out children小部件 - 然后没有通用的方法可以知道这些 children 是来自您的其他代码还是来自生成的代码。
此外,所有用户界面都必须在 QMainWindow
之上设计。换出基于另一种小部件类型的界面是行不通的,因为基础小部件是 QMainWindow
,需要 centralWidget
。这是一个 hack,但它有效:
// https://github.com/KubaO/Whosebugn/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
objects。额外的间接是无用的。
2. 使用强类型枚举而不是魔法常量来表示选择。
唉,我们不需要诉诸黑客。我们可以使用 QStackedWidget
来交换可见窗格。
首先,让我们制作一个 UiWidget
class 来包装给定的 Ui::
类型及其相应的小部件。它会自动在小部件上设置 children,将自己添加到堆叠小部件 parent,并有一个助手将其设置为堆栈中的当前小部件。
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
class 都可以基于不同的小部件类型,例如Ui::MainMenu
可以基于 QMainWindow
但例如Ui::OptionMenu
可以基于 QDialog
.
MainMenu
现在可以简单地成为一个包含 sub-widgets 及其 ui 结构的 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
成员 are-a 他们各自的 Ui::Class
,例如uiMainMenu
is-a Ui::MainMenu
.
此时,如果setAppearance
可以做成non-public方法,则不需要任何间接,可以直接对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 下均有效。