Qt - QStackedLayout setCurrentIndex displays blank window instead of widget - qt

I just started learning Qt a couple days ago to make a game, and I'm trying to figure out how to make layouts work.
What I want is a Window using QStackedLayout with 2 widgets inside: StartScreen and GameScreen. StartScreen is shown on top on program run. A button in StartScren is connected to a function inside Window, and Window will call setCurrentIndex to change the widget on top to GameScreen.
What happens right now is that when I click on the button, the view changes to a blank window. I've tried hardcoding to setCurrentIndex(0), which does nothing, setCurrentIndex(1), which is what GameScreen should be and displays the same blank window, and setCurrentIndex(2), which is out of index bounds but still does nothing. So the connection is going through, but I don't understand why a blank window will show up instead of the button I have on GameScreen.
If someone can explain to me what concept I missed and how to fix it, I'd greatly appreciate it. Thanks!
Here is window.cpp:
Window::Window(QWidget *parent) : QWidget(parent)
{
resize(640, 480);
layout = new QStackedLayout;
createStartScreen();
createGameScreen();
setLayout(layout);
show();
};
void Window::createStartScreen(){
start = new StartScreen();
layout->addWidget(start);
start->setWindow(this);
}
void Window::playGame(){
layout->setCurrentIndex(layout->indexOf(game));
}
void Window::createGameScreen(){
game = new GameScreen();
layout->addWidget(game);
}
startscreen.cpp:
StartScreen::StartScreen(QWidget *parent) : QWidget(parent)
{
newGameButton = new QPushButton("New Game", this);
newGameButton->setGeometry(QRect(QPoint(260, 300), QSize(120,40)));
quitButton = new QPushButton("Quit", this);
quitButton->setGeometry(QRect(QPoint(260, 360), QSize(120,40)));
connect(quitButton, SIGNAL(clicked()), QApplication::instance(), SLOT(quit()));
};
void StartScreen::setWindow(Window *w){
connect(newGameButton, SIGNAL(clicked()), w, SLOT(playGame()));
}
gamescreen.cpp:
GameScreen::GameScreen(QWidget *parent) : QWidget(parent)
{
button = new QPushButton("Hi");
button->setGeometry(QRect(QPoint(260, 260), QSize(120,40)));
};

That's because you don't call show on button. But you should rather use a layout to handle it.
e.g.:
GameScreen::GameScreen(QWidget *parent)
: QWidget(parent)
{
button = new QPushButton(tr("Hi"));
QHBoxLayout *layout = new QHBoxLayout(this);
layout->addWidget(button);
};
button will have a size adapted to its text and will be cleaned positioned in GameScreen.
On a side note, you should rather add a signal to StartScreen to request a new game and do the connection in Window. That way you won't have a tight coupling between StartScreen and Window.

Related

Can someone give me a simple qt example?

I want to implement a simple QT example: click the QPushButton to display a paragraph of text.
Like this:
I know there are many ways to implement it, but I don't know what's wrong with my code.
QPushButton *btn = new QPushButton;
//btn->show();
btn->setParent(this);
btn->setText("button 1");
QLabel *la = new QLabel(this);
connect(btn,&QPushButton::clicked,la,&QLabel::setText("show me"));
Anyone who can help me?
The idea is to create the QLabel with a predefined text and hide it. On clicking the button, show it. So, QLabel::hide and QLabel::show could be used here. Just take care of the coordinates where you show the label because it would overlap the button itself without layout or proper coordinates.
Example (mainwindow.cpp, constructor):
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
setWindowTitle("My App"); // title bar text
QPushButton *btn = new QPushButton(this);
btn->setText("Click Me!");
QLabel *la = new QLabel(this);
la->setText("Show Me!"); // set label text in advance
la->move(100, 100); // set label position
la->hide(); // hide label on load
connect(btn, &QPushButton::clicked, la, &QLabel::show);
}
MainWindow::~MainWindow()
{
delete ui;
}
Output (after clicking the image):

Controlling a popup QWidget attached to a QToolButton

I want to get a Popup Widget to be shown when clicking on a QToolbutton.
This can be done by adding an action to the button itself.
The popup will contain a three buttons (update, create and cancel) and a text input field.
I have some sample code with only one button I have shared as a Github repository.
The most relevant part of the code is:
auto button = new QToolButton(this);
button->setText(" AA ");
auto popup = new Popup(button, this);
auto popupAction = new QWidgetAction(this);
popupAction->setDefaultWidget(popup);
button->setPopupMode(QToolButton::InstantPopup);
button->addAction(popupAction);
The result is as follow:
I have two issues I cannot solve:
Getting the popup widget to right align to the button.
Getting the popup widget to close when one of the buttons inside of it are clicked.
Right align the popup
There is already a similar question: Set position (to right) of Qt QPushButton popup menu.
I can add the suggested code:
void Popup::showEvent(QShowEvent*)
{
QPoint p = this->pos();
QRect geo = clickedButton->geometry();
this->move(p.x()+geo.width()-this->geometry().width(), p.y());
}
But only the content of the popup gets right aligned to the button, not the popup itself:
Closing the popup
If I click anywhere (but a widget) in the Popup it closes. I'm somehow fine with this.
But if I cannot manage to get a click on the button to close the popup.
I've tried to call the close() function but it only clears the content of the popup without closing it.
Can I get the button to trigger a signal and then close the popup?
I ask both questions as the same time, since they look very similar: both times it's the content and not the popup that is affected.
auto popup = new Popup(button, this);
auto popupAction = new QWidgetAction(this);
popupAction->setDefaultWidget(popup);
button->setPopupMode(QToolButton::InstantPopup);
button->addAction(popupAction);
Your Popup widget is not the actual popup, but just a widget inside the real popup.
So what you're moving is the widget inside the real popup and not the popup itself.
The solution in the question you linked to uses QMenu and it works with QMenu.
In your code replace
connect(button, &QToolButton::clicked, this, &MainWindow::showPopup);
auto updateButton = new QPushButton("Update");
auto popupAction = new QWidgetAction(this);
popupAction->setDefaultWidget(updateButton);
button->setPopupMode(QToolButton::InstantPopup);
button->addAction(popupAction);
with
button->setPopupMode(QToolButton::InstantPopup);
auto menu = new Popup(button, this);
auto action = new QAction("Test");
menu->addAction(action);
button->setMenu(menu);
Change your Popup class to extend/inherit QMenu, uncomment the showEvent method and remove everything from the constructor.
EDIT
You can set a layout to the qmenu and add widgets to it.
auto menuLayout = new QGridLayout();
auto menuBtn1 = new QPushButton("Btn1");
auto menuBtn2 = new QPushButton("Btn2");
auto menuBtn3 = new QPushButton("Btn3");
menuLayout->addWidget(menuBtn1, 0, 0);
menuLayout->addWidget(menuBtn2, 0, 1);
menuLayout->addWidget(menuBtn3, 1, 0);
button->setPopupMode(QToolButton::InstantPopup);
auto menu = new Popup(button, this);
menu->setLayout(menuLayout);
When you use a layout it has margins that prevent alignment.
The QMenu of the Popup can be accessed through kinship but this must be accessed when the button is pressed as this is created when it is first shown.
popup.h
#ifndef POPUP_H
#define POPUP_H
#include <QWidget>
class QToolButton;
class Popup : public QWidget
{
Q_OBJECT
public:
explicit Popup(QWidget* parent=nullptr);
Q_SIGNALS:
void clicked();
};
#endif
popup.cpp
#include "popup.h"
#include<QWidget>
#include<QVBoxLayout>
#include<QPushButton>
Popup::Popup(QWidget* parent)
: QWidget(parent)
{
auto layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->addStretch();
auto updateButton = new QPushButton("Update");
layout->addWidget(updateButton);
connect(updateButton, &QPushButton::clicked, this, &Popup::clicked);
}
mainwindow.h
MainWindow::MainWindow()
{
auto widget = new QWidget;
setCentralWidget(widget);
auto layout = new QHBoxLayout(widget);
layout->addStretch();
auto button = new QToolButton;
button->setText(" AA ");
layout->addWidget(button);
auto popup = new Popup;
auto popupAction = new QWidgetAction(this);
popupAction->setDefaultWidget(popup);
button->setPopupMode(QToolButton::InstantPopup);
button->addAction(popupAction);
connect(popup, &Popup::clicked, [popup](){
if(QWidget *p = popup->parentWidget())
p->close();
});
}

Menubar is not showing on the second window

I am trying to build an application that after getting some user input on the first window, pops up another window and displays some results. However, even though the menubar is visible on the first window, the menubar does not appear on the second window. The two windows are objects of different classes, but both classes are inherited from QMainWindow.
I have tried using the menuBar() function which returns a pointer for the menubar to add menus (this works for the first window). I also tried creating a new menubar object which didn't help either.
//MapWindow.h
class MapWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MapWindow(QWidget *parent = nullptr);
~MapWindow();
private:
QAction *vehicleAct;
QAction *missionAct;
QAction *backAct;
QMenu *toolMenu;
};
//MapWindow.cpp
MapWindow::MapWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MapWindow)
{
ui->setupUi(this);
setWindowState(Qt::WindowMaximized);
vehicleAct = new QAction("Vehicle Selection");
vehicleAct->setShortcut(Qt::CTRL + Qt::Key_V);
missionAct = new QAction("Mission Selection");
missionAct->setShortcut(Qt::CTRL + Qt::Key_M);
backAct = new QAction("Back");
backAct->setShortcut(Qt::CTRL + Qt::Key_B);
toolMenu = menuBar()->addMenu("Tools");
toolMenu->addAction(vehicleAct);
toolMenu->addAction(missionAct);
toolMenu->addAction(backAct);
}
MapWindow::~MapWindow() {
delete ui;
}
When I use the same code in the WelcomeWindow class which is also inherited from QMainWindow it works perfectly. However it doesn't even show a menubar in this second window.
I managed to find the problem. One of my widgets (QScrollArea) was located in the top left corner of the screen which was stopping the whole menubar from displaying for some reason. Moving the QScrollArea down a little bit has solved the problem.

QToolButton with text: Overwrite minimal height to minic regular button height

I am displaying QToolButtons with icon plus text (Qt::ToolButtonTextBesideIcon) outside of a tool bar. Each button has a QAction associated with it which determines the used icon and the displayed text. All those buttons are placed inside a QGridLayout. So far so good.
Unfortunately, it looks like that as soon as you add a QAction to a QToolButton, Qt automatically decides to shrink it down to the minimal size, which is not what I want given my QGridLayout. I used the following lines to correct that behavior in the horizontal dimension:
QToolButton* pButton = new QToolButton(0);
pButton->addDefaultAction(pAction);
pButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
pButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
However that still leaves my button with a smaller height than a regular (push) button. I already tried various other size policies without success.
How to best solve this issue? Is there a reliable way to determine the "normal" button height?
One idea I had was to create a regular dummy button, place it in the same layout and then read its size. This size could then be applied to my QToolButton and the dummy button would be destroyed again. Is there a more elegant / reliable way?
I do not understand what do you want to achieve.
Difference between QPushButton and QToolButton is that, QToolButton has implemented PopupMenu ( can be done easily for QPushButton also )
As I understand visual difference is only small arrow in lower right corner of QToolButton, when you use added QActions to QToolButton
This arrow is for me only difference between QToolButton and QPushButton. But maybe I am missing something.
From your examples ( QToolButton with icon + text: How to center both? )
it does not look like you want to use that popup feature. Thats why I do not understand, why to use QToolButton instead of QPushButtons.
In this example shows:
1) Same height of QToolButton and QPushButton
2) PopuMenu for QPushButton
As for me, I do not understand why to use QToolButton and try to make it look like QPushButton when it is simple to use QPushButton as QToolButton
#include <QtGui>
#include <QtWidgets>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// Prepare layout
QMainWindow *window = new QMainWindow;
QWidget *centralWidget = new QWidget(window);
QGridLayout *grid = new QGridLayout(centralWidget);
QTextEdit *textEdit = new QTextEdit();
window->setCentralWidget(centralWidget);
QAction *toolAction = new QAction(window->style()->standardIcon(QStyle::SP_MediaPlay), "ToolButton", window);
QObject::connect(toolAction, &QAction::triggered, [=]() {
qDebug() << "action";
});
QPushButton *pushButton = new QPushButton(toolAction->icon(), "PushButton1", window);
pushButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QPushButton *pushButton2 = new QPushButton(toolAction->icon(), "PushButton2", window);
pushButton2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QPushButton *pushButton3 = new QPushButton(toolAction->icon(), "PushButton2", window);
pushButton3->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QObject::connect(pushButton3, &QPushButton::released, [window, pushButton3, toolAction](){
QMenu menu;
menu.addAction(toolAction);
QPoint pos = window->mapToGlobal(pushButton3->pos());
pos += QPoint(0, pushButton3->height());
menu.exec(pos);
});
QObject::connect(pushButton, SIGNAL(pressed()), toolAction, SLOT(trigger()));
QObject::connect(pushButton2, SIGNAL(pressed()), toolAction, SLOT(trigger()));
QToolButton *toolButton = new QToolButton(window);
toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
toolButton->setText("Popup action");
toolButton->addAction(toolAction);
toolButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
QToolButton *toolButton2 = new QToolButton(window);
toolButton2->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
toolButton2->setDefaultAction(toolAction);
toolButton2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
toolButton->setMaximumHeight(pushButton->sizeHint().height());
toolButton->setMinimumHeight(pushButton->sizeHint().height());
toolButton2->setMaximumHeight(pushButton->sizeHint().height());
toolButton2->setMinimumHeight(pushButton->sizeHint().height());
grid->addWidget(textEdit ,0,0,1,2);
grid->addWidget(toolButton ,1,0,1,1);
grid->addWidget(pushButton ,1,1,1,1);
grid->addWidget(toolButton2 ,2,0,1,1);
grid->addWidget(pushButton2 ,2,1,1,1);
grid->addWidget(pushButton3 ,3,0,1,2);
window->show();
return a.exec();
}

QGraphicsView does not show anything in Qt 5.0

I am new to Qt 5.0 and I am trying to use a QGraphicsView called "missionView" of size 700x400 inside a QWidget to show a rectangle. However, nothing shows up inside the graphics view on running the application. Here is the constructor of the QWidget "MainView" where I am doing everything
MainView::MainView(QWidget *parent) :
QWidget(parent),
ui(new Ui::MainView)
{
ui->setupUi(this);
QGraphicsScene scene(0, 0, 500, 500);
QGraphicsRectItem* myrect = scene.addRect(QRectF(0,0,15,5),QPen(), QBrush());
ui->missionView->setScene(&scene);
ui->missionView->setVisible(true);
ui->missionView->show();
ui->missionView->update();
printf("QGraphicsScene scene's items: %d\n",scene.items().size());
for (int i = 0; i < scene.items().size(); i++) {
printf("%d\n",scene.items().at(i));
}
}
The last print statement does show that one item has been added but still nothing gets shown. I have tried an approach similar to this but this too dosent work. Can anyone please explain this.
You're creating the scene object on the stack.
Thus the scene will be destroyed right away at the end of the constructor and thus nothing will be shown.
Create the scene on the heap and/or make it a class member and it should work.

Resources