I have made a quick test program which doesn't seem to bring up anything when executed. Here is my code:
z_lock.h
#ifndef Z_LOCK_H
#define Z_LOCK_H
#include <QtGui>
class z_lock : public QMainWindow
{
Q_OBJECT
public:
z_lock();
private slots:
void password_check();
void quit();
private:
QStackedWidget *book;
QWidget *page1;
QWidget *page2;
QLineEdit *input;
};
#endif
z_lock.cpp
#include "z_lock.h"
z_lock::z_lock(){
book = new QStackedWidget;
//page1
page1 = new QWidget;
QLabel *label1 = new QLabel("Enter password.");
input = new QLineEdit;
QPushButton *goButton = new QPushButton("Go");
connect(goButton, SIGNAL(clicked()), this, SLOT(password_check()));
QHBoxLayout *layout1;
layout1 -> addWidget(label1);
layout1 -> addWidget(input);
layout1 -> addWidget(goButton);
page1 -> setLayout(layout1);
//page2
page2 = new QWidget;
QLabel *label2 = new QLabel("Welcome, you're in.");
QVBoxLayout *layout2;
layout2 -> addWidget(label2);
page2 -> setLayout(layout2);
book -> addWidget(page1);
book -> addWidget(page2);
setCentralWidget(book);
}
void z_lock::quit(){
close();
}
void z_lock::password_check(){
QString guess = input -> text();
if (guess == "apple"){
z_lock::book -> setCurrentIndex(1);
}
}
main.cpp
#include "z_lock.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
z_lock *that;
that -> show();
return app.exec();
}
Please, any help would do. I have carried out research about everything to do with QMainWindow and still nothing. This will probably be stupidly easy to solve and I'm sorry for such a basic question.
You have several issues in your code... Let us start with the most important:
You are allocating the object in your main.cpp on the heap which is a sort-of issue in itself, but even if you do that, you should use the new keyword for that in C++.
That being said, you really should not allocate it here on the heap: partially because it would leak the memory, but more importantly there is no need for it.
Therefore, replace these lines:
z_lock *that;
that -> show();
with simply this:
z_lock that;
that.show();
This is pretty much the same issue for your layouts here, too:
QHBoxLayout *layout1;
QVBoxLayout *layout2;
Please change them to:
QHBoxLayout *layout1 = new QHBoxLayout;
QVBoxLayout *layout2 = new QVBoxLayout;
Furthermore, you should not include the whole QtGui module like this:
#include <QtGui>
Just include the classes which you eventually need. Even though this is in some Qt 4 book, this is bad practice.
This method is also useless in your code since it does nothing except wrapping the existing slot:
void z_lock::quit(){
close();
}
Also, you may wish to use the tr() method for things like this to get translation for other languages:
QLabel *label1 = new QLabel("Enter password.");
z_lock *that;
that -> show();
The problem is that is an uninitialized pointer. It doesn't point to a valid z_lock instance for you to call methods on.
You need to do either allocate and construct a z_lock instance with new:
z_lock* that = new z_lock;
that->show();
Or, more simply, switch to stack allocation:
z_lock that;
that.show();
The same applies to the "layout" variables in the z_lock constructor.
This is an example of why it's a good idea to compile at a high warning level and fix warnings. The compiler can easily notice and point out the invalid use of uninitialized variables.
Related
My situation is, a big Qt project with many QWidget communication requirement.
For example, I've a QPushButton B and a QLabel L, I need to click thd button B to show some text on label L or hide it, etc. But the problem is neither of them can get each other's object pointer, because both of them located in a deep QWidget tree, maybe their grandgrandgrand parent widget was sibling, what ever.
So my option, is creating a global unique QObject, just for singal/slot forwad,like this:
class GlobalForward: public QObject
{
Q_OBJECT
public:
GlobalForward():QObject(null) {}
signal:
void SigForwardButton(bool toggle);
}
GlobalForward* gForwardObj;
int main(int argc, char* argv[])
{
QApplication app(argc, argv[]);
gForwardObj = new GlobalForward();
QWidget w;
QPushButton* button = new QPushButton(&w);
QWidget m;
QLabel* label = new QLabel(&m);
w.show();
m.show();
connect(button, &QPushButton::clicked, gForwardObj, &GlobalForward::SigForwardButton);
connect(gForwardObj, &GlobalForward::SigForwardButton, label, &QWidget::hide);
return app.exec();
}
Actually, the button and the label are so faraway that they cannot see each other. I want to use GlobalForward to concat the singnal to make it work . Further more, it can forwad QEvent silmiarly.
What I want to know is, is this way properly solved my problem, and what's the disadvantage of it. Also, any better solution will be appropriate, thanks.
So, I took it down a few notches and am trying simple programs to get the hang of Qt's tools.
First I tried a simple label inside main() function, then a button inside it. All good.
Next, I tried the same, but inside a main window (using the Qt's created documents). After the one-button program worked, I did a two-button program, that simple. Worked.
Then, I tried Qt's Box Layouts.
In none of these "main window" tries, I changed the main.cpp file created by Qt.
Here's the mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private:
QPushButton *button1;
QPushButton *button2;
};
#endif // MAINWINDOW_H
Next, the mainwindow.cpp file:
#include "mainwindow.h"
#include <QPushButton>
#include <QHBoxLayout>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
setWindowTitle(tr("Two Button Example"));
resize(400,300);
button1 = new QPushButton(tr("Bye!"));
button1->setGeometry(0,0,200,30);
//button1->setParent(this);
connect(button1,SIGNAL(clicked()),this,SLOT(close()));
button1->show();
button2 = new QPushButton(tr("Hide B1!"));
button2->setGeometry(0,0,200,30);
//button2->setParent(this);
connect(button2,SIGNAL(clicked()),button2,SLOT(hide()));
button2->show();
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(button1);
layout->addWidget(button2);
setLayout(layout);
}
MainWindow::~MainWindow()
{
}
I'm studying this book (for the layouts specifically, Chapter 2, the Find Dialog example (page 16 of the book, or 34 from the pdf file): C++ GUI Programming with Qt 4 1st ed.pdf
For this specific problem, I also used this: QHBoxLayout - Qt Examples and Tutorials
What I noticed:
Commenting the QHBoxLayout part, all the way to the "setLayout" function, makes no difference in the program. No test I've done is affected by this QBoxLayout thing;
The "setGeometry" function sets the position (first two parameters) and size (width and heightm, last two parameters) of a widget. This position is related to the parent widget, which may be the screen itself when no parent is assigned;
When the "setParent" functions for the buttons are commented and the "show" function is uncommented, the buttons are shown each in a separate window from the MainWindow. When I uncomment the "setParent" functions, the buttons are shown inside the MainWindow, and there's no difference if there are or no "show" functions for the buttons. The detail is, for the book I referenced, no example so far have had the need to declare the parent widgets, nor did the example in Qt's Examples and Tutorials site;
If I don't use the "setGeometry" function, the buttons are big, like 600x600, whatever. They follow the rules above regarding the other functions, but they are huge and always one on top of the other, not side by side. The function "sizeHint()" used extensively in the book, also has no effect;
Appearently, it's all following the same syntaxes and rules of the examples. So, what am I doing wrong, or not doing here? If it's not a problem to enlight me with the "sizeHint()" function, too, that would be great.
Thanks in advance!
I don't even remember the origin of the problem, but I have never been able to work with the layouts right in the window. In company I worked with Qt we used a central widget for managing layouts, so the diagram is: window -> central widget -> layouts -> subwidgets. If I modified your code so, it would look like this:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
setWindowTitle(tr("Two Button Example"));
resize(400,300);
auto *parentWidget = new QWidget;
auto button1 = new QPushButton(tr("Bye!"), parentWidget);
button1->setGeometry(0,0,200,30);
connect(button1,SIGNAL(clicked()),this,SLOT(close()));
auto button2 = new QPushButton(tr("Hide B1!"), parentWidget);
button2->setGeometry(0,0,200,30);
connect(button2,SIGNAL(clicked()),button2,SLOT(hide()));
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(button1);
layout->addWidget(button2);
parentWidget->setLayout(layout);
setCentralWidget(parentWidget);
}
Also, I don't quite understand why do you try to set the button size if the layout resets it later. To change the size inside a layout you must change the sizeHint of a widget:
setWindowTitle(tr("Two Button Example"));
resize(400,300);
auto *parentWidget = new QWidget;
auto button1 = new QPushButton(tr("Bye!"), parentWidget);
button1->setMaximumSize(20, 20);
connect(button1,SIGNAL(clicked()),this,SLOT(close()));
auto button2 = new QPushButton(tr("Hide B1!"), parentWidget);
button2->setMaximumSize(20, 20);
connect(button2,SIGNAL(clicked()),button2,SLOT(hide()));
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(button1);
layout->addWidget(button2);
parentWidget->setLayout(layout);
setCentralWidget(parentWidget);
After some observation, I believe I know why it wasn't working.
In the examples provided, the person was using QDialog or applying the layouts straight to the main() code, while I was using the QMainWindow class.
I suppose that when you use a QMainWindow class, you always have to set a Central Widget in order to make layouts work (setting this central widget layout).
So a solution would be:
QWidget centerWidget = new QWidget();
setCentralWidget(centerWidget);
//Add buttons, lists, and whatever
/*Add a QHBoxLayout or a QVBoxLayout and use
layout->addWidget() to add the widgets created,
like the examples. Then:*/
centerWidget->setLayout(layout);
It works, like Victor Polevoy suggested. Just answering as I believe I understood why weren't my QLayouts working.
I am following Qt examples (like TabDialog) and I notice that all the UI items are created as pointers - yet I see no delete and no destructor.
Is that right ? Will that not lead to memory leak ?
I am trying to add destructors
~TabDialog()
{
delete tabWidget;
delete buttonBox;
}
and on caller
TabDialog *tabDialog = new TabDialog();
tabDialog->setAttribute(Qt::WA_DeleteOnClose);
tabDialog->exec();
But the program crashes when I close the dialog.
Are the destructors, and delete all pointer items, unnecessary or am I doing it wrong ?
I think that you are confused because of these lines:
tabWidget = new QTabWidget;//and so on
You don't see explicit parent (like new QTabWidget(this);), but it is not necessary here. Take a look here:
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(tabWidget);
mainLayout->addWidget(buttonBox);
setLayout(mainLayout);
setLayout will reparent your QVBoxLayout and QVBoxLayout will reparent all widgets inside it, so now your widgets has a parent and they will be destroyed after your dialog.
As doc said:
When you use a layout, you do not need to pass a parent when
constructing the child widgets. The layout will automatically reparent
the widgets (using QWidget::setParent()) so that they are children of
the widget on which the layout is installed.
Note: Widgets in a layout are children of the widget on which the
layout is installed, not of the layout itself. Widgets can only have
other widgets as parent, not layouts.
I'm sorry, but this just doesn't reproduce. The test case is below. You'd probably need to add some extra code to make it reproduce.
Qt's memory management will take care of everything, since all of the widgets end up having parents. Namely:
Tabs are parented as soon as they are passed to addTab.
tabWidget and buttonBox are parented as soon as they are added to the layout.
Since you delete the tabWidget and buttonBox before Qt attempts to delete them, everything is fine. As soon as you delete them, QObject's memory management is informed and they are removed from the TabDialog's child list. I've made this point explicit in the destructor code.
The meaning of Q_ASSERT is: "At this point during runtime, the following must be true". If we're wrong, the debug build will abort. Since it doesn't, the assertions are correct. Thus, before delete tabWidget, the dialog has both QTabWidget and QDialogButtonBox children. After delete tabWidget, the dialog should not have any QTabWidget children anymore. And so on.
#include <QApplication>
#include <QDialog>
#include <QTabWidget>
#include <QDialogButtonBox>
#include <QVBoxLayout>
class TabDialog : public QDialog
{
QTabWidget *tabWidget;
QDialogButtonBox *buttonBox;
public:
TabDialog() :
tabWidget(new QTabWidget),
buttonBox(new QDialogButtonBox(QDialogButtonBox::Ok |
QDialogButtonBox::Cancel))
{
tabWidget->addTab(new QWidget, tr("General"));
tabWidget->addTab(new QWidget, tr("Permissions"));
tabWidget->addTab(new QWidget, tr("Applications"));
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(tabWidget);
layout->addWidget(buttonBox);
setLayout(layout);
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
}
~TabDialog() {
Q_ASSERT(findChild<QTabWidget*>());
Q_ASSERT(findChild<QDialogButtonBox*>());
delete tabWidget;
Q_ASSERT(! findChild<QTabWidget*>());
Q_ASSERT(findChild<QDialogButtonBox*>());
delete buttonBox;
Q_ASSERT(! findChild<QTabWidget*>());
Q_ASSERT(! findChild<QDialogButtonBox*>());
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TabDialog *tabDialog = new TabDialog();
tabDialog->setAttribute(Qt::WA_DeleteOnClose);
tabDialog->exec();
return 0;
}
The only way it'd crash is if you tried the following:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TabDialog *tabDialog = new TabDialog();
tabDialog->setAttribute(Qt::WA_DeleteOnClose);
tabDialog->exec();
// At this point `tabDialog` is a dangling pointer.
delete tabDialog; // crash
return 0;
}
Unfortunately, Qt examples are a case of pointless premature pessimization. Qt's classes utilize the PIMPL idiom extensively. Thus the size of, say a QTabWidget is not much larger than that of QObject (48 vs. 16 bytes on my 64 bit platform). By allocating the fixed members of your class on the heap, you're performing two heap allocations: a small one for the QObject-derived class, and then another one for its PIMPL. You're doubling the number of allocations for no good reason.
Here's how to avoid this pessimization:
#include <QApplication>
#include <QDialog>
#include <QTabWidget>
#include <QDialogButtonBox>
#include <QVBoxLayout>
class TabDialog : public QDialog
{
QVBoxLayout m_layout;
QTabWidget m_tabWidget;
QDialogButtonBox m_buttonBox;
QWidget m_generalTab, m_permissionsTab, m_applicationsTab;
public:
TabDialog() :
m_layout(this),
m_buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel)
{
m_tabWidget.addTab(&m_generalTab, tr("General"));
m_tabWidget.addTab(&m_permissionsTab, tr("Permissions"));
m_tabWidget.addTab(&m_applicationsTab, tr("Applications"));
m_layout.addWidget(&m_tabWidget);
m_layout.addWidget(&m_buttonBox);
connect(&m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(&m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
auto tabDialog = new TabDialog();
tabDialog->setAttribute(Qt::WA_DeleteOnClose);
tabDialog->show(); // NOT tabDialog->exec()!!
return app.exec();
}
The less explicit heap allocations, the better. This way you can't even be tempted to delete anything in the destructor, since there are no pointers involved. The compiler generates necessary destructor calls for you automatically.
Furthermore, if you're showing only one window in main, there's no point to explicit heap allocation either:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
TabDialog tabDialog;
tabDialog.show();
return app.exec();
}
Memory handling on Qt
Qt handles the widgets as a tree, every widget has a parent and every parent has
the obligation to free the children memory, if a widget has no parent you should delete it manually with the operator delete.
(Note: I started learning Qt yesterday and I did my search before asking this.)
After a bit of playing with Qt Designer I decided to make a more serious program, all programatically. Whereas before, simple taskes seemed.. simple, now, diaplaying a button is hell of a complicated because it doesn't show up.
main.cpp
int main(int argc, char * argv[])
{
QApplication app(argc, argv);
PixelPeep p;
p.show();
return app.exec();
}
pixelpeep.h - relevant part
class PixelPeep : public QMainWindow
{
Q_OBJECT
public:
explicit PixelPeep(QWidget *parent = 0);
signals:
public slots:
private:
QToolBar * toolBar;
QHBoxLayout * toolbarLayout;
QToolButton * addButton; // add new image
QScrollBar * zoomBar;
};
pixelpeep.cpp - relevant part
PixelPeep::PixelPeep(QWidget *parent) :
QMainWindow(parent)
{
resize(600,375);
toolBar = new QToolBar;
addButton = new QToolButton;
addButton->setGeometry(20,20,20,20);
toolBar->addWidget(addButton);
toolbarLayout = new QHBoxLayout;
toolbarLayout->addWidget(addButton);
}
After all this, I get an empty window.
Possible cause, AFAIK:
button would go out of scope after being created in the class constructor - it's not the case here because it's dynamically allocated and pointer by the private member addButton
not being in a layout or having size 0 - it's not the case since both of these were addressed in the code
What else could it be?
Sorry for such a noob question...
Call addToolBar(toolBar); inside the PixelPeep constructor.
You didn't set any icon on your button so it will appear as invisible. Hover over it and you will see it's there:
I'm a little new to object oriented programming, and very new to Qt and GUIs in general.
I am now playing with this example in Nokia's Qt tutorial:
http://doc.qt.nokia.com/4.1/tutorial-t5.html
I tried to extend the code; this is what I have now:
#include <QApplication>
#include <QFont>
#include <QLCDNumber>
#include <QPushButton>
#include <QSlider>
#include <QVBoxLayout>
#include <QWidget>
class MyWidget : public QWidget
{
public:
MyWidget(QWidget *parent = 0);
};
MyWidget::MyWidget(QWidget *parent)
: QWidget(parent)
{
QPushButton *quit = new QPushButton(tr("Quit"));
move(1600,0);
quit->setFont(QFont("Times", 18, QFont::Bold));
QPushButton* numbase;
numbase = new QPushButton[4];
numbase[0].setText("Dec");
(numbase+1)->setText("Bin"); // Hihihi
numbase[2].setText("Hex");
numbase[3].setText("Oct");
// a[i] == *(a+i)
QLCDNumber *lcd = new QLCDNumber(8);
lcd->setSegmentStyle(QLCDNumber::Filled);
lcd->setMode(QLCDNumber::Hex);
QSlider *slider = new QSlider(Qt::Horizontal);
slider->setRange(0, 99);
slider->setValue(0);
connect(quit, SIGNAL(clicked()), qApp, SLOT(quit()));
connect(slider, SIGNAL(valueChanged(int)),
lcd, SLOT(display(int)));
connect(numbase+0, SIGNAL(clicked()), lcd, SLOT(setDecMode()));
connect(numbase+1, SIGNAL(clicked()), lcd, SLOT(setBinMode()));
connect(numbase+2, SIGNAL(clicked()), lcd, SLOT(setHexMode()));
connect(numbase+3, SIGNAL(clicked()), lcd, SLOT(setOctMode()));
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(quit);
layout->addWidget(lcd);
layout->addWidget(slider);
// Segmentation fault if I include those two lines:
for(int i=0;i<4;i++)
layout->addWidget(numbase+i);
// -------------
setLayout(layout);
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MyWidget widget;
widget.show();
return app.exec();
}
When I include the marked two lines, the program does its job correctly, but if I quit it (and only then) the console shows a "Segmentation fault".
I'd like to know why that happens.
And also, is there a better way to reference the 4 widgets? (numbase+2) looks weird, is this really how I am supposed to do this?
The possible reason of crash is that Qt, at deletion of the form, calls delete function for each widget on the form. So, at program exit, when destroying your form, Qt internals would execute the following sequence:
delete numbase[0];
delete numbase[1];
delete numbase[2];
delete numbase[3];
However, the correct way to free the memory would be
delete [] numbase;
because you allocated numbase with a special array allocation new[] operator, which is different from the usual new. And it's "undefined behavior" if you use delete instead of delete[] in C++. So, segmentation fault could happen if it's the case.
You can check if it's the reason, by allocating each emenent in your array separately, with new QPushButton;, as specified in the other answer.
A better way is to use a array of pointers, you should try it:
QPushButton *buttons[4];
buttons[0] = new QPushButton(this);
buttons[1] = new QPushButton(this);
buttons[2] = new QPushButton(this);
buttons[3] = new QPushButton(this);
buttons[0]->setText("foo")
connect(buttons[0], SIGNAL(clicked()), this, SLOT(bar()));
Try replacing 4 by 3 in the loop.
If you want Qt to do garbage collection make sure you set the parent child relationship of widget correctly i.e. QPushButton resides in QWidget so set it in constructor.
For simple UI like these try creating it using Qt Designer and see the generated code.