Qt - Compiler complains when invoking setLayout() on my MainWindow - c++

I wanna learn how to create a gui by hand without the designer. I tried to add a layout to my MainWindow but when running it says
QWidget::setLayout: Attempting to set QLayout "" on MainWindow "", which already has a layout
This is my code :
//Header
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private:
QHBoxLayout *layout;
};
//Constructor in my *.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
layout = new QHBoxLayout;
this->setLayout(layout);
}
//The usual main function
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
What is wrong? I did what my book said. I even looked up some code on the internet which was really hard to find somehow and it was still the same. I just cannot add a layout to my window.

There's a similar question which helped me find out what's wrong. Thanks to Mat for his link to that question.
What every QMainWindow needs is a QWidget as central widget. I also created a new Project with the designer, compiled it and looked the ui_*.h files up.
So every QMainWindow should look similar to this :
//Header
class MainWindow : public QMainWindow
{
Q_OBJECT
QWidget *centralWidget;
QGridLayout* gridLayout;
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private:
};
//*.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
centralWidget = new QWidget(this);
this->setCentralWidget( centralWidget );
gridLayout = new QGridLayout( centralWidget );
}
Now you don't add / set the layout to the MainWindow. You add / set it to the centralWidget.

Related

Widget initialized later will not show in MainWindow

I am learning Qt using Qt 5.13 on MacOS.
First I define MyWidget inherited from QWidget. MyWidget has a QPushButton, but this button will be created in a slot function called 'fresh', not in constructor.
I add MyWidget in MainWindow (inherited from QMainWindow), and defined another button_2 to emit signal to callMyWidget's 'fresh' function to create button.
If I did not hide MyWidget in MainWindow first, MyWidget's button will not show. If I hide MyWidget first, everything seems OK.
I hope to know the reason. Thanks
I tried to repaint or update MyWidget in 'fresh' function, but did not help.
mywidget.h
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QWidget>
#include<QPushButton>
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = nullptr);
~MyWidget();
public slots:
void fresh();
private:
QPushButton* myButton;
};
#endif // WIDGET_H
mywidget.cpp
#include "mywidget.h"
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent)
{
}
MyWidget::~MyWidget()
{
}
void MyWidget::fresh()
{
myButton = new QPushButton(this);
myButton->setStyleSheet("QPushButton { background-color: green;}");
show();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include"mywidget.h"
#include<QHBoxLayout>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
signals:
public slots:
private:
MyWidget* myWidget;
};
#endif // MAINWINDOW_H
MainWindow.cpp
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
QWidget* qwidget = new QWidget;
myWidget = new MyWidget(this);
QPushButton* button = new QPushButton("Show",this);
QHBoxLayout* mainLayout = new QHBoxLayout;
mainLayout->addWidget(myWidget);
mainLayout->addWidget(button);
qwidget->setLayout(mainLayout);
setCentralWidget(qwidget);
//myWidget->hide();
connect(button,&QPushButton::clicked,myWidget,&MyWidget::fresh);
}
main.cpp
#include "mywidget.h"
#include"mainwindow.h"
#include<QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
If I add myWidget->hide(); in mainwindow.cpp, it seems right.
If I remove it, the green button will not show, even if I repaint or update or show in fresh function.
'mywidget' is taking the whole space, if you want to know if it is taking all the space or not :
mywidget->setStyleSheet("* {border: 4px solid orange;}")
Since you are using a layout, you might want to determine the minimum size of a QPushButton. the QPushButton has a default horizontal size policy : "Minimum", by default. Maybe if you set the minimum width using this function : "setMinimumWidth(int width)", might fix your problem.
Also, don't forget to call this :
myButton->show();
Every object that inherits from QObject should be shown with this func ".show".
Here is all the flags for QSizePolicy will help you understand what is going on in layouts (layouts work a lot with QSizePlicy flags) : https://doc.qt.io/qt-5/qsizepolicy.html#Policy-enum .
Unless you don't want the layout, you have to specify the position and the size in this way :
mywidget->setGeometry(QPoint(x, y), QSize(width, height));
and the same thing for your buttons.

Qt5 custom QDialog without using qt creator

I am trying to write a custom pure C++ QDialog so that I can create a base class and inherit from it later. The follow is the code that shows a QLabel in a QDialog:
"EDLController.h"
#ifndef EDLController_h
#define EDLController_h
#include <QDialog>
class EDLController : public QDialog {
Q_OBJECT
public:
EDLController(QWidget *parent = nullptr);
};
#endif
"EDLController.cpp"
EDLController::EDLController(QWidget *parent) : QDialog(parent) {
QVBoxLayout vBoxLayout;
QLabel label("text");
vBoxLayout.addWidget(&label);
setLayout(&vBoxLayout);
setWindowTitle("test");
}
"main.cpp"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
EDLController *w = new EDLController();
w->show();
return app.exec();
}
However, it shows an empty window with correct title:
image
The program is running on Raspberry Pi (Raspbian). Can anyone help me to find out the problem.
The problem is your label declaration. You create a local variable label wich is destroyed at the end of the EDLController constructor.
You can confirm that by inheriting QLabel like this :
class MyLabel : public QLabel
{
Q_OBJECT
public:
MyLabel(const QString& str, QWidget* parent = nullptr) : QLabel(str,parent){}
~MyLabel() {qDebug() << "LABEL DELETED";}
};
The "LABEL DELETED" message will be logged when the QDialog is instanced.
And, of course, you cannot display a deleted widget.
The correct code is the following :
QLabel* label = new QLabel("text");
vBoxLayout.addWidget(label);
The label will be destoyed when the parent item (your dialog) will be destroyed.
vBoxLayout and label will be destroyed when EDLController constructor exits because they are local variables. Create new instances on heap to avoid that:
EDLController::EDLController(QWidget *parent)
: QDialog(parent)
{
QVBoxLayout * vBoxLayout = new QVBoxLayout(this);
QLabel * label = new QLabel(this);
label->setText("test");
vBoxLayout->addWidget(label);
setLayout(vBoxLayout);
setWindowTitle("test");
}

QPushButton not clickable in MainWindow

I'm writing a QT project in Xcode, I made a Widget application in the QT Editor and used the "qmake -spec macx-xcode" to convert the project into an Xcode project.
I have a standard project:
main.cpp
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow w;
w.show();
return app.exec();
}
main window.cpp
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
m_button = new QPushButton(this);
m_button -> setText("button");
m_button->setGeometry(QRect(QPoint(100, 100),QSize(200, 50)));
QPushButton *workingButton = new QPushButton("Hello");
workingButton -> show();
connect(m_button, SIGNAL(clicked()), this, SLOT(quitButton()));
ui->setupUi(this);
}
void MainWindow::quitButton() {
m_button->setText("Example");
}
MainWindow::~MainWindow()
{
delete ui;
}
main window.h
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void quitButton();
private:
Ui::MainWindow *ui;
QPushButton *m_button;
};
#endif
The m_button shows up in the mainWindow but it is not clickable but the workingButton, shows up in its own separate window, and in the connect, when I replace the m_button with the workingButton, it is able to call the function. Any idea why the m_button is not sending a signal or function not being called?
The reason is quite simple: you have other transparent widgets overlaid on top of m_button. You must ensure that the button is not covered by anything else. E.g. move the creation of the button after the setupUi call or make the button a child of the central widget. Generally speaking, the setupUi call should be the first thing in a widget's constructor.
You also don't need to dynamically allocate the child widgets: prefer holding things by value: less things can go wrong then, and you're having less overhead, too!
Thus, pretending that the Ui_MainWindow class was really generated by uic:
// https://github.com/KubaO/stackoverflown/tree/master/questions/simple-button-main-41729401
#include <QtWidgets>
class Ui_MainWindow {
public:
QWidget *central;
QGridLayout *layout;
QLabel *label;
void setupUi(QMainWindow *parent);
};
class MainWindow : public QMainWindow, private Ui_MainWindow {
Q_OBJECT
QPushButton m_button{"Click Me"};
public:
MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
setupUi(this);
m_button.setParent(centralWidget());
m_button.setGeometry({{50, 50}, m_button.sizeHint()});
}
};
void Ui_MainWindow::setupUi(QMainWindow *parent) {
central = new QWidget{parent};
layout = new QGridLayout{central};
label = new QLabel{"Hello"};
label->setAlignment(Qt::AlignCenter);
label->setStyleSheet("background-color:blue; color:white;");
layout->addWidget(label, 0, 0);
parent->setCentralWidget(central);
parent->setMinimumSize(200, 200);
}
int main(int argc, char ** argv) {
QApplication app{argc, argv};
MainWindow w;
w.show();
return app.exec();
}
#include "main.moc"

Parent issue (?) while using derived Qt widget class

The following is the smallest example I could make to present the isuue.
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
QVBoxLayout *vLayout = new QVBoxLayout(this);
QGroupBox *gb = new QGroupBox;
// MyGroupBox *gb = new MyGroupBox;
vLayout->addWidget(gb);
QPushButton *btB = new QPushButton;
vLayout->addWidget(btB);
}
The code above produces the image above, it's just a group box and a button in vertical layout.
If I replace QGropBox by MyGroupBox then it doesn't show there anymore. The code below produces the image below.
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
QVBoxLayout *vLayout = new QVBoxLayout(this);
// QGroupBox *gb = new QGroupBox;
MyGroupBox *gb = new MyGroupBox;
vLayout->addWidget(gb);
QPushButton *btB = new QPushButton;
vLayout->addWidget(btB);
}
Where mygroupbox.h (the constructor body is empty in the .cpp file):
#include <QWidget>
class MyGroupBox : public QWidget
{
Q_OBJECT
public:
explicit MyGroupBox(QWidget *parent = 0);
signals:
public slots:
};
Why the group box isn't showing there? How to make it appear?
This is why it doesn't appear:
class MyGroupBox : public QWidget
Your "group box" is basically just a QWidget. Inherit from QGroupBox instead.
As an aside, a minimal example could look like the below. Not a single declaration/statement/expression can be removed. The button aids in visualizing the problem, so it should be left in. The use of a failure trigger variable highlights exactly what condition triggers the failure: the code self-documents and you almost need no narrative to explain it. The question could be as concise as the test case below and one sentence "Why is the group box's border not visible when fail is true?". Most likely, had you followed the minimization fully through, you'd realize yourself what the problem was - it becomes rather obvious. It's not so when MyGroupBox is declared in another file!
The technique of putting it all into a single main.cpp file is critical in spotting the problem: all of the code is physically next to each other, making it much easier to spot mistakes! When you minimize, usually the first things that have to go are separate files: cram it all into one file, and then relentlessly remove absolutely everything that's not directly needed in reproducing the issue.
#include <QtWidgets>
struct MyGroupBox : public QWidget {};
int main(int argc, char ** argv) {
bool fail = true;
QApplication app{argc, argv};
QWidget widget;
QVBoxLayout layout{&widget};
QGroupBox groupBox;
MyGroupBox myGroupBox;
QPushButton button;
layout.addWidget(fail ? static_cast<QWidget*>(&myGroupBox) : &groupBox);
layout.addWidget(&button);
widget.show();
return app.exec();
}
This concise style is not only for trivial test cases. In your real code, the Widget's header and implementation could look as follows:
// Widget.h
#include <QtWidgets>
#include "MyGroupBox.h"
class Widget : public QWidget {
Q_OBJECT
QVBoxLayout layout{this};
MyGroupBox groupBox;
QPushButton button{tr("Click Me!")};
public:
explicit Widget(QWidget * parent = nullptr);
};
// Widget.cpp
#include "Widget.h"
Widget::Widget(QWidget * parent) :
QWidget{parent} {
layout.addWidget(&groupBox);
layout.addWidget(&button);
}
If you insist on shielding the interface from implementation details, don't use pointers to widgets etc., use a PIMPL.
// Widget.h
#include <QWidget>
class WidgetPrivate;
class Widget : public QWidget {
Q_OBJECT
Q_DECLARE_PRIVATE(Widget)
QScopedPointer<WidgetPrivate> const d_ptr;
public:
explicit Widget(QWidget * parent = nullptr);
};
// Widget.cpp
#include "Widget.h" // should always come first!
#include "MyGroupBox.h"
class WidgetPrivate {
Q_DECLARE_PUBLIC(Widget)
Widget * const q_ptr;
public:
QVBoxLayout layout{q_func()};
QGroupBox groupBox;
MyGroupBox myGroupBox;
QPushButton button{"Click Me!"};
WidgetPrivate(Widget * q) : q_ptr(q) {
layout.addWidget(&groupBox);
layout.addWidget(&button);
}
};
Widget::Widget(QWidget * parent) :
QWidget{parent}, d_ptr{new WidgetPrivate{this}}
{}

C++ - Adding item to QListWidget from QPushButton

Hello I am trying to add items to a QListWidget from a QPushButton. Both the QListWidget and QPushButton are added as individual widgets inside of a QGraphicsScene. I want the effect of a box that fills with text lines
main.c
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
QGraphicsView view;
QGraphicsScene *scene = new QGraphicsScene(0, 0, 1200, 1200, &view);
scene->setBackgroundBrush(Qt::gray);
view.setScene(scene);
QPushButton *PushButton1;
PushButton1 = new QPushButton();
PushButton1->setGeometry(QRect(19, 20, 154, 4));
QListWidget *ListWidget;
ListWidget = new QListWidget;
scene->addWidget(ListWidget);
scene->addWidget(PushButton1);
QObject::connect(PushButton1, SIGNAL(clicked()),&w, SLOT(handleClick(*QListWidget)));
view.show();
return a.exec();
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::handleClick(QListWidget *List)
{
int test;
List->addItem("TESTING");
//QApplication::quit();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QListWidget>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
private slots:
public slots:
void handleClick(QListWidget *List);
};
#endif // MAINWINDOW_H
This code compiles fine. How I get the following error in the console when the application is running
QObject::connect: No such slot MainWindow::handleClick(*ListWidget) in ..\MenuTest\main.cpp:48
Can someone help me do this? I've seen several tutorials but it's using the designer to make the GUI and I'd like to know how to do it in code without using designer. Thanks.
Your slot accepts QListWidget but you're connecting with ListWidget as the parameter, the signature has to be an exact match due to the way signals and slots work in Qt.
Put handleClick under public slots: and change this line:
QObject::connect(PushButton1, SIGNAL(clicked()),&w, SLOT(handleClick(*ListWidget)));
To this:
QObject::connect(PushButton1, SIGNAL(clicked()),&w, SLOT(handleClick(*QListWidget)));
Update:
I see I missed a key point, the signatures have to match, as in parameter to parameter, so the line up there will not work.
To fix this remove the parameter completely, since PushButton1 can't send it automatically.
QObject::connect(PushButton1, SIGNAL(clicked()),&w, SLOT(handleClick()));
Also remove it here:
void MainWindow::handleClick()
To access the QListWidget you'll have to reference it directly, either by passing it to MainWindow's constructor or iterating the window's controls.