I have a class called UserInterface. In this class there are different functions. build_start_screen() adds all the widgets (labels, buttons,...) for the initial startscreen. build_option_a_screen() removes everything from the startscreen and adds all the widgets needed for the screen when the users click on the button for option A, and so on. The class is stripped down for the sake of this question.
Now I have declared a button in build_start_screen() and connected it to a simple MessageBox.exec() so it should pop-up on click.
However, nothing happens when the button gets clicked.
What am I doing wrong? Has it something to do with the lifetime of variables expiring after the function finishes?
#include <QApplication>
#include <QPushButton>
#include <QAbstractButton>
#include <QLabel>
#include <QFont>
#include <QVBoxLayout>
#include <QMessageBox>
//Class handling all the UI in this Application
class UserInterface {
public:
//Build the initial UI the user sees
void build_start_screen(QWidget& window) {
//Make new QVBoxLayout for this startscreen UI
this->layout = new QVBoxLayout(&window);
//Test messagebox
QMessageBox msgBox;
msgBox.setText("Button test.");
//Button to go to Option A-screen
QPushButton* showMsgBox = new QPushButton("Show pop-up");
QAbstractButton::connect(showMsgBox, SIGNAL (clicked()), &window, SLOT (msgBox.exec()));
//Add labels and button to QVBoxLayout
layout->addWidget(showMsgBox);
}
private:
//Properties
QVBoxLayout* layout;
};
int main(int argc, char **argv) {
QApplication app (argc, argv);
//Initialize Window
QWidget Window;
Window.resize(400, 250);
//Create new UserInterface object
//This will allow us to create different user-interfaces
//depending on the function we call
UserInterface* ui = new UserInterface();
ui->build_start_screen(Window);
Window.show();
return app.exec();
}
And what if I'd like to do the same, but instead of calling a messageBox I'd like to call another function?
#include <QApplication>
#include <QPushButton>
#include <QAbstractButton>
#include <QLabel>
#include <QFont>
#include <QVBoxLayout>
#include <QMessageBox>
//Class handling all the UI in this Application
class UserInterface {
public:
//Build the initial UI the user sees
void build_start_screen(QWidget& window) {
//Make new QVBoxLayout for this startscreen UI
this->layout = new QVBoxLayout(&window);
//Test messagebox
QMessageBox msgBox;
msgBox.setText("Button test.");
//Button to go to Option A-screen
QPushButton* showMsgBox = new QPushButton("Show pop-up");
QAbstractButton::connect(showMsgBox, SIGNAL (clicked()), &window, SLOT (build_option_a_screen()));
//Add labels and button to QVBoxLayout
layout->addWidget(showMsgBox);
}
void build_option_a_screen(QWidget& window) {
//Do stuff here with window
//e.g
window.resize(500, 500);
}
private:
//Properties
QVBoxLayout* layout;
};
int main(int argc, char **argv) {
QApplication app (argc, argv);
//Initialize Window
QWidget Window;
Window.resize(400, 250);
//Create new UserInterface object
//This will allow us to create different user-interfaces
//depending on the function we call
UserInterface* ui = new UserInterface();
ui->build_start_screen(Window);
Window.show();
return app.exec();
}
Your code has 2 problems:
The window "object" does not have slot "msgBox.exec()" as pointed out by the error:
QObject::connect: No such slot QWidget::msgBox.exec() in ../main.cpp:23
Correcting the above, the solution would be:
QObject::connect(showMsgBox, &QPushButton::clicked, &msgBox, &QMessageBox::exec);
but now the problem is that "msgBox" is a local variable that will be destroyed and cannot be displayed.
So the solution is to make msgBox a member of the class or a pointer (in the case of the pointer you must manage the dynamic memory to avoid memory leaks):
//Class handling all the UI in this Application
class UserInterface {
public:
//Build the initial UI the user sees
void build_start_screen(QWidget& window) {
//Make new QVBoxLayout for this startscreen UI
this->layout = new QVBoxLayout(&window);
msgBox.setText("Button test.");
//Button to go to Option A-screen
QPushButton* showMsgBox = new QPushButton("Show pop-up");
QObject::connect(showMsgBox, &QPushButton::clicked, &msgBox, &QMessageBox::exec);
//Add labels and button to QVBoxLayout
layout->addWidget(showMsgBox);
}
private:
//Properties
QVBoxLayout* layout;
QMessageBox msgBox;
};
Plus:
It is recommended not to use the old connection syntax as it has limitations and hides the problems.
It is recommended not to use the old connection syntax as it has limitations and hides the problems.
If you want to connect to a method of some kind that is not a QObject (for example X as you want the OP) then the solution is to use a lambda method:
//Class handling all the UI in this Application
class UserInterface {
public:
//Build the initial UI the user sees
void build_start_screen(QWidget& window) {
//Make new QVBoxLayout for this startscreen UI
this->layout = new QVBoxLayout(&window);
//Button to go to Option A-screen
QPushButton* showMsgBox = new QPushButton("Show pop-up");
QObject::connect(showMsgBox, &QPushButton::clicked, [this, &window](){
build_option_a_screen(window);
});
//Add labels and button to QVBoxLayout
layout->addWidget(showMsgBox);
}
void build_option_a_screen(QWidget& window) {
//Do stuff here with window
//e.g
window.resize(500, 500);
}
private:
//Properties
QVBoxLayout* layout;
};
Related
I have this very strange issue regarding a QMenu and its position when execing.
Here is the code for my subclassed QMenu:
DockItemContextMenu::DockItemContextMenu(QWidget *parent) : QMenu(parent){
style = qApp->style();
QPointer<QAction> restoreAction = new QAction(QIcon(style->standardIcon(QStyle::SP_TitleBarMaxButton)), "Restore", this);
QPointer<QAction> minimizeAction = new QAction(style->standardIcon(QStyle::SP_TitleBarMinButton), "Minimize", this);
QPointer<QAction> maximizeAction = new QAction(style->standardIcon(QStyle::SP_TitleBarMaxButton), "Maximize", this);
QPointer<QAction> stayOnTopAction = new QAction("Stay On Top", this);
stayOnTopAction->setCheckable(true);
QPointer<QAction> closeAction = new QAction(style->standardIcon(QStyle::SP_TitleBarCloseButton), "Close", this);
this->addActions({restoreAction, minimizeAction, maximizeAction, stayOnTopAction, closeAction});
connect(restoreAction, &QAction::triggered, parent, [this](){ emit restoreTriggered();}, Qt::QueuedConnection);
connect(minimizeAction, &QAction::triggered, parent, [this](){ emit minimizeTriggered();}, Qt::QueuedConnection);
connect(maximizeAction, &QAction::triggered, parent, [this](){ emit maximizeTriggered();}, Qt::QueuedConnection);
connect(stayOnTopAction, &QAction::triggered, parent, [this](){ emit stayOnTopTriggered();}, Qt::QueuedConnection);
connect(closeAction, &QAction::triggered, parent, [this](){ emit closeTriggered();}, Qt::QueuedConnection);
}
Okay, so essentially I have another widget who holds an instance of this DockItemContextMenu as a field. In this owning class, called Titlebar, I made it such that doing a right click will emit the customContextMenuRequested(QPoint) signal.
TitleBar::TitleBar(QString title, QWidget *parent){
...
this->setContextMenuPolicy(Qt::CustomContextMenu);
contextMenu = new DockItemContextMenu(this);
connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)), Qt::QueuedConnection);
...
}
After this, this widget is essentially inserted into a QGraphicsScene and is converted implicitly into a QGraphicsItem. When I do the FIRST right click event on my Titlebar it will not exec at the correct screen position if I dragged the MainWindow of the entire QApplication anywhere other than its starting position on screen. In addition to being in a QGraphicsScene, this scene itself is always stored in a QSplitter. Now I would understand if this always had some sort of issue, but it turns out, every time I call the slot for that signal, ONLY the first time will it exec in the incorrect position in the QGraphicsScene. No matter how I manipulate the size of the Titlebar widget itself, move commands or maximize commands to the MainWindow, or even edit the splitter size for the QGraphicsView that affects the size of the QGraphicsScene, it will always be in the correct position afterwards. here is the function for execing:
void TitleBar::showContextMenu(QPoint point){
qDebug() << point;
contextMenu->exec(point);
emit _parent->focusChangedIn();
}
I printed the point at which it is calling the exec. The strangest part is that both times I right click in the same location, it will print the SAME value for the slot's positional parameter both the first exec and second exec, but be in the correct location every time other than the first. Did I forget to set some other flag when I added the context menu to the Titlebar class? Does it have anything to do with setting the QMenu's parent to the Titlebar? I'm just dumbfounded how the same QPoint could exec at two different screen locations given the same value. Does anybody have a clue what may or may not be happening on the first call to the Titlebar's slot for execing the QMenu?
EDIT: The issue stemmed from doing this line of code in the Titlebar constructor:
contextMenu = new DockItemContextMenu(this);
Changing it to:
contextMenu = new DockItemContextMenu;
fixed the issue. Does anyone know why, or is this possibly a bug? I rather not accept this as an answer because it does not explain why it happened in the first place.
Here is a minimal example with the same effect.
MainWindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QWidget>
#include <QGraphicsView>
#include <QSplitter>
#include <QHBoxLayout>
#include <QGraphicsScene>
#include <QPointer>
#include <QTreeWidget>
#include "titlebar.h"
class MainWindow : public QMainWindow{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private:
};
#endif // MAINWINDOW_H
MainWindow.cpp:
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent){
QPointer<QWidget> widgetArea = new QWidget;
QPointer<QHBoxLayout> hLayout = new QHBoxLayout;
widgetArea->setLayout(hLayout);
QPointer<QSplitter> splitter = new QSplitter;
hLayout->addWidget(splitter);
QPointer<QTreeView> tree = new QTreeView;
splitter->addWidget(tree);
QPointer<QGraphicsView> view = new QGraphicsView;
splitter->addWidget(view);
splitter->setStretchFactor(0, 1);
splitter->setStretchFactor(1, 4);
QPointer<QGraphicsScene> scene = new QGraphicsScene;
view->setScene(scene);
QPointer<Titlebar> blue = new Titlebar;
blue->setObjectName("blue");
blue->setStyleSheet(QString("#blue{background-color: rgb(0,0,255)}"));
blue->resize(250,250);
scene->addWidget(blue);
this->setCentralWidget(widgetArea);
this->resize(1000,750);
}
MainWindow::~MainWindow(){
}
Titlebar.h:
#ifndef TITLEBAR_H
#define TITLEBAR_H
#include <QMenu>
#include <QWidget>
#include <QPointer>
#include <QDebug>
#include <QMouseEvent>
class Titlebar : public QWidget{
Q_OBJECT
public:
explicit Titlebar(QWidget *parent = nullptr);
QPointer<QMenu> menu;
QPoint currentPos;
protected slots:
void mousePressEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
void showContextMenu(QPoint point);
};
#endif // TITLEBAR_H
Titlebar.cpp:
#include "titlebar.h"
Titlebar::Titlebar(QWidget *parent) : QWidget(parent){
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)), Qt::QueuedConnection);
menu = new QMenu(this);
menu->addAction("Test");
}
void Titlebar::showContextMenu(QPoint point){
qDebug() << point;
menu->exec(mapToGlobal(point));
}
void Titlebar::mouseMoveEvent(QMouseEvent *event){
if (event->buttons() && Qt::LeftButton){
QPoint diff = event->pos() - currentPos;
move(pos() + diff);
}
}
void Titlebar::mousePressEvent(QMouseEvent * event){
currentPos = event->pos();
}
main.cpp:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
So this runs and reproduces the error accordingly. If you change the line in Titlebar.cpp from
menu = new QMenu(this);
to:
menu = new QMenu;
Then it works correctly. ONLY the first right click to open the context menu will spawn in the incorrect location on screen. All subsequent right clicks will now follow either the widget/window/splitter in any combination. I don't get it, can someone tell me if this is actually a bug or not.
You need to add one line of code because your using a QGraphicsProxyWidget which is part of a QGraphicsScene. The scene is represented by a QGraphicsView which inherits QAbstractScrollArea. This causes the context menu to be shown via the viewport and not the widget itself. Therefore adding this one line of code will override the title bar to not be embedded in the scene when it's parent was already embedded in the scene. Effectively making it reference the widget again and not the viewport.
In the MainWindow.cpp right after line 26 add
blue->setWindowFlags(Qt::BypassGraphicsProxyWidget);
I have an error, just after closing my application, I get an error message saying that it has stopped unexpectedly. I reached this:
But, after I close the window, I get this error:
The program has unexpectedly finished. The process was ended
forcefully.
My project structure is:
And my code is:
main.cpp
#include "mainwindow.h"
#include <QApplication>
#include <windows/login.h>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Login w;
w.show();
return a.exec();
}
util_window.h
#ifndef UTIL_H
#define UTIL_H
#include <QObject>
#include <QMainWindow>
class UtilWindow : QObject
{
public:
// vars
// methods
util();
void setCenterWindow(QMainWindow* w);
};
#endif // UTIL_H
util_window.cpp
#include "util_window.h"
#include <QDesktopWidget>
UtilWindow::util()
{
}
void UtilWindow::setCenterWindow(QMainWindow *w) {
int width = w->frameGeometry().width();
int height = w->frameGeometry().height();
QDesktopWidget wid;
int screenWidth = wid.screen()->width();
int screenHeight = wid.screen()->height();
w->setGeometry((screenWidth/2)-(width/2),(screenHeight/2)-(height/2),width,height);
w->show();
}
login.h
#ifndef LOGIN_H
#define LOGIN_H
#include <QMainWindow>
#include <QObject>
#include <QWidget>
#include "util/util_window.h"
#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
#include <QFormLayout>
class Login : public QMainWindow
{
Q_OBJECT
public:
// vars
UtilWindow* util;
// widgets
// labels
QLabel* lblHost;
QLabel* lblPort;
QLabel* lblUser;
QLabel* lblPass;
// textbox
QLineEdit* leHost;
QLineEdit* lePass;
QLineEdit* leUser;
QLineEdit* lePort;
// layouts
QFormLayout* layout;
QWidget* central;
QVBoxLayout* mainLayout;
QGroupBox* gbCredentials;
// methods
void initLabels();
void initTextBoxes();
void initUI();
explicit Login(QWidget *parent = nullptr);
~Login();
signals:
public slots:
};
#endif // LOGIN_H
login.cpp
#include "login.h"
Login::Login(QWidget *parent) : QMainWindow(parent)
{
this->util = new UtilWindow();
this->initUI();
}
Login::~Login() {
delete this->lblHost;
delete this->lblPass;
delete this->lblPort;
delete this->lblUser;
delete this->leHost;
delete this->lePass;
delete this->lePort;
delete this->leUser;
delete this->layout;
delete this->central;
delete this->mainLayout;
delete this->gbCredentials;
delete this->util;
}
void Login::initUI() {
this->setFixedSize(400,400);
this->setWindowTitle(tr("Inicio de sesión"));
this->util->setCenterWindow(this);
this->initLabels();
this->initTextBoxes();
this->layout = new QFormLayout();
this->layout->addRow(this->lblHost, this->leHost);
this->layout->addRow(this->lblPort, this->lePort);
this->layout->addRow(this->lblUser, this->leUser);
this->layout->addRow(this->lblPass, this->lePass);
this->gbCredentials = new QGroupBox();
this->gbCredentials->setTitle(tr("Datos de conexión"));
this->gbCredentials->setLayout(layout);
this->mainLayout = new QVBoxLayout();
this->mainLayout->addWidget(gbCredentials);
this->central = new QWidget();
this->central->setParent(this);
this->central->setLayout(this->mainLayout);
this->setCentralWidget(this->central);
}
void Login::initLabels() {
this->lblHost = new QLabel();
this->lblPass = new QLabel();
this->lblPort = new QLabel();
this->lblUser = new QLabel();
this->lblHost->setText(tr("Host: "));
this->lblPass->setText(tr("Contraseña: "));
this->lblPort->setText(tr("Puerto: "));
this->lblUser->setText(tr("Usuario: "));
}
void Login::initTextBoxes() {
this->leHost = new QLineEdit();
this->lePass = new QLineEdit();
this->lePort = new QLineEdit();
this->leUser = new QLineEdit();
this->leHost->setPlaceholderText(tr("Host de MySQL"));
this->lePass->setPlaceholderText(tr("Ingrese su contraseña"));
this->leUser->setPlaceholderText(tr("Ingrese su nombre de usuario"));
this->lePort->setPlaceholderText(tr("Ingrese el número de puerto"));
this->leHost->setToolTip(this->leHost->placeholderText());
this->leUser->setToolTip(this->leUser->placeholderText());
this->lePass->setToolTip(this->lePass->placeholderText());
this->lePort->setToolTip(this->lePort->placeholderText());
}
Thanks in advance!
When you add widgets to a layout, e.g.
this->layout.addRow(&(this->lblHost), &(this->leHost));
you're parenting them to the layout's widget, in this case your Login main window, and when the parent widget destructor will be called, all children widget will be delete'd. What happens in your code is: the children (line edits and labels) are not instantiated with new, so calling delete on them will crash your application. You should replace the children declarations in your widget header with pointers, this way:
QLabel * lblHost;
// ...
QLineEdit * leHost;
// etc
and instantiate them before adding them to the layout:
this->lblHost = new QLabel();
this->leHost = new QLineEdit();
this->layout.addRow(this->lblHost, this->leHost);
//etc
This applies to all layouts and widgets that have a parent (i.e. central Qwidget).
Also: explicitly calling delete on children is not needed, because the parent destructor will take care of that, and this applies to the central widget as well:
QMainWindow takes ownership of the widget pointer and deletes it at
the appropriate time
As thuga points out in comments, there is nothing inherently wrong in instantiating children on the stack, as long as their destructor is called before their parent's, thus they are automatically removed from the parent's children list and the parent destructor will not call delete on them.
As explained here, this is legit:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget widget;
QLabel label(&widget);
widget.show();
return a.exec();
}
because label will go out of scope before widget.
Changing the two objects creation order will cause the application crash at exit time:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QLabel label;
QWidget widget;
label.setParent(&widget);
widget.show();
return a.exec();
}
You have deleting QMainWindow elements by yourself :
delete this->lblHost;
When your QObject inherited objects are created on heap with parent, you should not delete it by yourselves. because QObject tree will take care of the memory memory management.
So twice deleting would be happen.
What is the procedure to make a text being written in a QLineEdit widget, dynamically display inside a QTextEdit that already contains some text?
For example, let us say that a QLineEdit asks for a name where one writes "John". Is it possible to display it in real time inside a QTextEdit containing :
The name is + textFromQLineEdit + , age 24 ?
The displayed text has to dynamically take into account the changes being made to the QLineEdit so that the user does not need to press a button or press enter to see his/her name appear.
The following is the minimal code for connecting the two widgets to each other using the signal textChanged() from QLineEdit and the slot setText() from QTextEdit (which does not allow for adding some text before and after the text from the QLineEdit) :
#include <QLineEdit>
#include <QVBoxLayout>
#include <QGroupBox>
#include <QTextEdit>
#include <QApplication>
class SmallWindow : public QWidget
{
Q_OBJECT
public:
SmallWindow();
private:
QLineEdit *nameLine;
QTextEdit *textBox;
};
SmallWindow::SmallWindow() : QWidget()
{
setFixedSize(300,250);
QLineEdit *nameLine = new QLineEdit;
QTextEdit *textBox = new QTextEdit;
QWidget::connect(nameLine,SIGNAL(textChanged(QString)),textBox,SLOT(setText(QString)));
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(nameLine);
layout->addWidget(textBox);
QGroupBox *group = new QGroupBox(this);
group->setLayout(layout);
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
SmallWindow window;
window.show();
app.exec();
}
#include "main.moc"
What should be done to keep the text before and after the QLineEdit text in place and updating the QTextEdit box in real time?
Create special slot:
void SmallWindow::pasteText(const QString& str)
{
textBox->setText(QString("The name is %1 , age 24").arg(str));
}
and don't use textChanged() signal because you need only one name accepted by user, so you need QLineEdit::editingFinished() (or maybe QLineEdit::returnPressed(), it depends on your needs)
connect(nameLine,SIGNAL(editingFinished(QString)),this,SLOT(pasteText(QString)));
Also you don't need QWidget::connect, because you write this code inside QObject subclass, so it is not necessary.
Also these lines:
QLineEdit *nameLine = new QLineEdit;
QTextEdit *textBox = new QTextEdit;
should be:
nameLine = new QLineEdit;
textBox = new QTextEdit;
Create an own slot for the text update. I think that you have also some errors in your code.
Widgets nameLine and textBox are already defined in the SmallWindow.h. You probably want to create them in SmallWindow.cpp following way:
nameLine = new QLineEdit;
textBox = new QTextEdit;
Also GroupBox group is not set to any layouts. Perhaps you want to create one layout more and set the widget there?
QVBoxLayout *mainlayout = new QVBoxLayout;
mainlayout->addWidget(group);
this->setLayout(mainlayout);
If you create an own slot for the text update, you can just change text content of the textBox there:
SmallWindow.h
#ifndef SMALLWINDOW_H
#define SMALLWINDOW_H
#include <QLineEdit>
#include <QVBoxLayout>
#include <QGroupBox>
#include <QTextEdit>
class SmallWindow : public QWidget
{
Q_OBJECT
public:
SmallWindow();
private slots:
void updateLineEditText(QString name);
private:
QLineEdit *nameLine;
QTextEdit *textBox;
};
#endif // SMALLWINDOW_H
SmallWindow.cpp
#include "SmallWindow.h"
SmallWindow::SmallWindow() : QWidget()
{
setFixedSize(300,250);
nameLine = new QLineEdit;
textBox = new QTextEdit;
connect(nameLine,SIGNAL(textChanged(QString)),this,
SLOT(updateLineEditText(QString)));
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(nameLine);
layout->addWidget(textBox);
QGroupBox *group = new QGroupBox(this);
group->setLayout(layout);
QVBoxLayout *mainlayout = new QVBoxLayout;
mainlayout->addWidget(group);
this->setLayout(mainlayout);
}
void SmallWindow::updateLineEditText(QString name) {
QString textEditString("The name is ");
textEditString.append(name);
textEditString.append(", age 24 ?");
textBox->setText(textEditString);
}
This is a minimal example in Qt 5, using C++11. It is about as concise as it would be in Python. If you are using Qt 5, then your question should have looked exactly as it does below, save for the connect statement. This is what "minimal" means when it comes to Qt. Avoid the fluff and boilerplate that doesn't add to the problem. There's no need to have a separate class for the window in such a simple example.
#include <QVBoxLayout>
#include <QLineEdit>
#include <QTextEdit>
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget window;
QVBoxLayout layout(&window);
QLineEdit name;
QTextEdit text;
layout.addWidget(&name);
layout.addWidget(&text);
QObject::connect(&name, &QLineEdit::textChanged, [&](const QString & name){
text.setPlainText(QString("The name is %1, age 24.").arg(name));
});
window.show();
return app.exec();
}
The same in Qt 4 is below - note the absence of any manual memory management. That's how your question ideally should have looked for Qt 4, without the slot's implementation of course. You can use the connectSlotsByName or an explicit connect, of course - that's just a matter of style.
#include <QVBoxLayout>
#include <QLineEdit>
#include <QTextEdit>
#include <QApplication>
class Window : public QWidget {
Q_OBJECT
QVBoxLayout m_layout; // not a pointer!
QLineEdit m_name; // not a pointer, must come after the layout!
QTextEdit m_text;
Q_SLOT void on_name_textChanged(const QString & name) {
m_text.setPlainText(QString("The name is %1, age 24.").arg(name));
}
public:
Window() : m_layout(this) {
m_layout.addWidget(&m_name);
m_layout.addWidget(&m_text);
m_name.setObjectName("name");
QMetaObject::connectSlotsByName(this);
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Window window;
window.show();
return app.exec();
}
#include "main.moc"
I have a custom widget with some standard child widgets inside. If I make a separate test project and redefine my custom widget to inherit QMainWindow, everything is fine. However, if my custom widget inherits QWidget, the window opens, but there are no child widgets inside.
This is the code:
controls.h:
#include <QtGui>
#include <QVBoxLayout>
#include <QLineEdit>
#include <QPushButton>
class Controls : public QWidget
{
Q_OBJECT
public:
Controls();
private slots:
void render();
private:
QWidget *frame;
QWidget *renderFrame;
QVBoxLayout *layout;
QLineEdit *rayleigh;
QLineEdit *mie;
QLineEdit *angle;
QPushButton *renderButton;
};
controls.cpp:
#include "controls.h"
Controls::Controls()
{
frame = new QWidget;
layout = new QVBoxLayout(frame);
rayleigh = new QLineEdit;
mie = new QLineEdit;
angle = new QLineEdit;
renderButton = new QPushButton(tr("Render"));
layout->addWidget(rayleigh);
layout->addWidget(mie);
layout->addWidget(angle);
layout->addWidget(renderButton);
frame->setLayout(layout);
setFixedSize(200, 400);
connect(renderButton, SIGNAL(clicked()), this, SLOT(render()));
}
main.cpp:
#include <QApplication>
#include "controls.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Controls *controls = new Controls();
controls->show();
return app.exec();
}
This opens up a window with correct dimensions, but with no content inside.
Bear in mind this is my first day using Qt. I need to make this work without inheriting QMainWindow because later on I need to put this on a QMainWindow.
You're missing a top level layout:
Controls::Controls()
{
... (yoour code)
QVBoxLayout* topLevel = new QVBoxLayout(this);
topLevel->addWidget( frame );
}
Or, if frame is not used anywhere else, directly:
Controls::Controls()
{
layout = new QVBoxLayout(this);
rayleigh = new QLineEdit;
mie = new QLineEdit;
angle = new QLineEdit;
renderButton = new QPushButton(tr("Render"));
layout->addWidget(rayleigh);
layout->addWidget(mie);
layout->addWidget(angle);
layout->addWidget(renderButton);
setFixedSize(200, 400);
connect(renderButton, SIGNAL(clicked()), this, SLOT(render()));
}
Note that setLayout is done automatically when QLayout is created (using parent widget)
You'll want to set a layout on your Controls class for managing its child sizes. I'd recommend removing your frame widget.
controls.cpp
Controls::Controls()
{
layout = new QVBoxLayout(this);
.
.
.
}
main.cpp
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
MainWindow w;
w.show();
return app.exec();
}
I have the following minimal example code given.
main.cpp:
#include <QApplication>
#include "qt.h"
int main(int argc, char** argv)
{
QApplication app(argc, argv);
MyDialog mainWin;
mainWin.show();
return app.exec();
}
qt.cpp:
#include <QLabel>
#include "qt.h"
void MyDialog::setupUi()
{
setCentralWidget(new QWidget);
mainLayout = new QVBoxLayout( centralWidget() );
centralWidget()->setLayout(mainLayout);
// show the add new effect channel button
QPushButton* newKnobBtn = new QPushButton("new", this );
connect( newKnobBtn, SIGNAL(clicked()), this, SLOT(addNewKnob()));
mainLayout->addWidget( newKnobBtn, 0, Qt::AlignRight );
containerWidget = new QWidget(this);
scrollArea = new QScrollArea(containerWidget);
mainLayout->addWidget(containerWidget);
scrollLayout = new QVBoxLayout(scrollArea);
scrollArea->setLayout(scrollLayout);
/*
QSizePolicy pol;
pol.setVerticalPolicy(QSizePolicy::Expanding);
setSizePolicy(pol);
*/
addNewKnob(); // to fit size initially
}
void MyDialog::addNewKnob()
{
scrollLayout->addWidget(new QLabel("Hello World", this));
/*
containerWidget->adjustSize();
adjustSize();
*/
}
qt.h:
#include <QMainWindow>
#include <QVBoxLayout>
#include <QScrollArea>
#include <QPushButton>
class MyDialog : public QMainWindow
{
Q_OBJECT
private slots:
void addNewKnob();
private:
void setupUi();
QVBoxLayout* mainLayout;
QScrollArea* scrollArea;
QVBoxLayout* scrollLayout;
QWidget* containerWidget;
public:
MyDialog( ) { setupUi(); }
};
Compiling: Put all in one directory, type
qmake -project && qmake && make
I have the adjustSize() solution from here, but it does not work: (link). Everything I commented out was things I tried but did not help.
How do I make containerWidget and scrollLayout grow correctly, when a new Label is being added to scrollLayout?
Here's a simplified version that works for me:
qt.cpp:
#include <QLabel>
#include <QPushButton>
#include <QScrollArea>
#include "qt.h"
MyDialog::MyDialog()
{
QWidget * mainWidget = new QWidget;
QBoxLayout * mainLayout = new QVBoxLayout(mainWidget);
setCentralWidget(mainWidget);
// show the add new effect channel button
QPushButton* newKnobBtn = new QPushButton("new");
connect( newKnobBtn, SIGNAL(clicked()), this, SLOT(addNewKnob()));
mainLayout->addWidget( newKnobBtn, 0, Qt::AlignRight );
QScrollArea * scrollArea = new QScrollArea;
scrollArea->setWidgetResizable(true);
mainLayout->addWidget(scrollArea);
QWidget * labelsWidget = new QWidget;
labelsLayout = new QVBoxLayout(labelsWidget);
scrollArea->setWidget(labelsWidget);
addNewKnob(); // to fit size initially
}
void MyDialog::addNewKnob()
{
labelsLayout->addWidget(new QLabel("Hello World"));
}
qt.h:
#include <QMainWindow>
#include <QBoxLayout>
class MyDialog : public QMainWindow
{
Q_OBJECT
public:
MyDialog( );
private slots:
void addNewKnob();
private:
QBoxLayout * labelsLayout;
};
You have containerWidget that contain only one QScrollArea. I don't know why do you need this. But if you need this for some reason, you need to add a layout to this widget in order to make layouts work. Also do not create a layout for QScrollArea. It already have internally implemented layout. You should add scrollLayout to the scroll area's viewport() widget instead.
When you construct a layout and pass a widget to its constructor, the layout is automatically assigned to the passed widget. You should not call setLayout after that. This action will take no effect and produce console warning.