Don't understand why I can't call setCentralWidget in QMainWindow subclass - c++

//clposter.h
class CLPoster : public QMainWindow
{
Q_OBJECT
public:
CLPoster();
private slots:
QWidget createCentralWidget();
void createActions();
void createMenu();
void createStatusBar();
void loadSavedPosts();
};
//clposter.cpp
CLPoster::CLPoster()
{
setWindowTitle("Craigslist Poster");
QWidget mainWidget = createCentralWidget();
setCentralWidget(mainWidget);
// createActions();
// createMenu();
// createStatusBar();
// loadSavedPosts();
// checkForActionsNeeded(); //May want to break up into more functions
}
The error I'm getting is this:
/usr/include/qt4/QtGui/qwidget.h:: In constructor ‘CLPoster::CLPoster()’:
/usr/include/qt4/QtGui/qwidget.h:787: error: ‘QWidget::QWidget(const QWidget&)’ is private
/home/brett/projects/CLPoster/CLPoster-build-desktop/../CLPoster/clposter.cpp:9: error: within this context
/home/brett/projects/CLPoster/CLPoster-build-desktop/../CLPoster/clposter.cpp:10: error: no matching function for call to ‘CLPoster::setCentralWidget(QWidget&)’
/usr/include/qt4/QtGui/qmainwindow.h:141: candidates are: void QMainWindow::setCentralWidget(QWidget*)
I'm having trouble interpreting the error message. It says there is no matching function call, but it should be inheriting it from QMainWindow. It could just be a lack of understanding C++ more so than QT, first time I've used it, but dunno. Thanks for the help.

All QWidget items must be allocated in the free-store (new) since they all have to have "parents". In Qt a parent will delete its children (with delete). This is why any function returning, accepting, whatever a widget is going to do so on a pointer to a widget, not the widget itself; you need to do the same.

The setCentralWidget function expects that you are sending a pointer to a QWidget (QWidget*), whereas you are trying to send the actual object (or a reference to the object, QWidget&, as implied by the compiler error) in your code. If you create your central widget as a pointer (change the member function to QWidget* createCentralWidget()) and pass the pointer to the setCentralWidget function you should be good to go.
e.g.
QWidget* CLPoster::createCentralWidget()
{
QWidget* w = new QWidget;
// Do stuff..
return w;
}
Then in your constructor, you can just call setCentralWidget(createCentralWidget()). The QMainWindow destructor will ensure that your central widget is deleted.

Don't you need to allocate the Widget on the heap and pass a pointer to setCentralWidget()

Related

What are `(QWidget* pobj=0)` and `(QWidget* pwgt/*=0/)` for?

I have just started to learn QT. Can't understand how do theese constructors work. For example:
//Progress.h
#include<QtWidgets>
class QProgressBar;
class Progress:public QWidget{
Q_OBJECT
private:
QProgressBar* m_pprb;
int step;
public:
Progress(QWidget* pobj=0);
public slots:
void slotStep();
void slotReset();
//
//Progress.cpp
#include<QtWidgets>
#include"Progress.h"
Progress::Progress(QWidget* pwgt/*=0*/):QWidget(pwgt)
{
//some buttons
}
So, the question is, what happens in constructors?
What you are creating is a Progressclass which inherits from QWidget.
The QWidget class can take an parentargument, if you look at the documentation:
Constructs a widget which is a child of parent, with widget flags set to f. If parent is nullptr, the new widget becomes a window. If parent is another widget, this widget becomes a child window inside parent. The new widget is deleted when its parent is deleted.
This parentin your code is called pwgt (I would think it stands for parentWidget)
What you do, is creating a default argument for your constructor to be set automatic to 0:
Progress(QWidget* pobj=0);
Cleaner maybe would be (for convenience with Qt standard):
Progress(QWidget* parent=nullptr);
So, why do do you need the constructor to look like this?
It is the same reason, which stands for QWidget:
You can set a parent widget, but you don't have to!
The QWidget class will deal for you with this, either you set a parent or not.

Put arguments in slots in Qt

I've made a class named MyWindow which inherits from QWidget to create a window. Here is the content of mywindow.h:
class MyWindow: public QWidget{
public:
MyWindow(QString title,QString icon,int w = 600,int h = 400);
int getWidth() const;
int getHeight() const;
public slots:
void openDialogBox(QString title,QString message);
private:
int m_width;
int m_height;
};
There is a openDialogBox slot which takes the title and the message of the dialog box as arguments.
I've made a menu bar which basically looks like this:
MyWindow myWindow("Example window",QCoreApplication::applicationDirPath() + "/icon.png");
QMenuBar menuBar(&myWindow);
menuBar.setGeometry(0,0,myWindow.getWidth(),menuBar.geometry().height());
QMenu *fileMenu = new QMenu("&File");
QAction *fileMenu_open = fileMenu->addAction("&Open");
MyWindow::connect(fileMenu_open,&QAction::triggered,&myWindow,&MyWindow::openDialogBox);
In the last line, I would like to send arguments to the slot &MyWindow::openDialogBox. I tried to do:
MyWindow::connect(fileMenu_open,&QAction::triggered,&myWindow,&MyWindow::openDialogBox("Title","Hello, this is a message"));
but it didn't work (I don't need you to explain why it didn't work, I already know why). How to do this properly so that it works?
Since you are using the New Signal Slot Syntax, I would suggest using a c++11 lambda instead of a slot, and call the desired function inside your slot, here is how your connect call would look like:
QObject::connect(fileMenu_open, &QAction::triggered, &myWindow, [&myWindow](){
myWindow.openDialogBox("Title","Hello, this is a message");
});
Note that openDialogBox is not required to be a slot this way, it can be any normal function.
If your compiler does not support C++11 lambda expression, you might have to declare a slot that does not take any argument, and connect to that slot. And inside that slot call your function with the desired arguments. . .
Use lambdas
QObject::connect(fileMenu_open, &QAction::triggered, &myWindow, [QWeakPointer<MyWindow> weakWindow = myWindow]()
{
weakWindow->openDialogBox("Title","Hello, this is a message");
});
QWeakPointer is used in case your class is moved, so the "old" myWindow is a dangling pointer
If your class won't be moved, just capture myWindow.
Note that my code needs C++14 to declare a variable in the lambda capture

Do I have to delete these pointers?

This is the MainWindow class which I call and use the function show() to make it visible to the user.
class MainWindow : public QMainWindow
{
Q_OBJECT
QWidget *centralWidget;
QGridLayout* gridLayout;
QGridLayout* infoBoxLayout;
QHBoxLayout* buttonGroup;
QHBoxLayout* subCategoryLayout;
//... more widgets
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
void setupUi();
void setupConnections();
private slots:
void add();
void edit();
void remove();
void find();
void clearAll();
void screenshotDesktop();
void screenshotApp();
void currentSubCategoryChanged( const QString& );
void curretCategoryChanged( const int );
void keyPressEvent( QKeyEvent * );
};
I created for each widget (those pointers after the macro Q_OBJECT) a new object on the heap with new. However, I did not delete them anywhere in the program. Does this cause a memory leak in Qt? Or does something from Qt delete them automatically when destroying the class?
If a widget has a parent set, then Qt will handle deleting the widget.
In the case of a MainWindow, when you close it, the MainWindow and its children will be cleaned up, so pass the parent to the widget's constructor: -
QHBoxLayout* buttonGroup = new QHBoxLayout(this); // where this is the parent (MainWindow)
If you create a Widget such as this: -
QHBoxLayout* buttonGroup = new QHBoxLayout;
And haven't passed in the parent, then it will not be cleaned up and you'll have to handle that yourself.
if you add them to the gui hierarchy then they will be cleaned up when the MainWindow is deleted
this is because parents assume ownership over their children (which is set with the various adds of the gui)
so a this->add(centralWidget); will call centralWidget->setParent(this); which will let centralWidget be deleted when MainWindow is deleted
you are free to delete QObjects yourself but beware dangling pointers (QPointer will help here). though I suggest using deleteLater() to ensure no strange behavior when a pointer still lives on the stack.
for more info about the object tree see here
The automatic memory management through parent-child relationships is done by the QObject. QWidget happens to be a QObject, and it so happens that widgets that have parent widgets have the same underlying QObjects as parents.
A QObject with children automatically deletes its children in its destructor.
A QObject or a QWidget may be adopted by another object. For example, adding widgets to a layout will automatically reparent them to the widget the layout is set on. Even if a layout doesn't have a widget set on it yet, the reparenting will be done at the time you add the layout to a widget (or to a layout that has a widget set). It's pretty clever and saves a lot of typing and reduces possibilities for mistakes.
The idiomatic, minimum-typing way of adding widgets to another widget is:
MyWidget() {
QLayout * layout = new QHBoxLayout(this); // set a layout on this widget
layout->addWidget(new QLabel("foo")); // the label is reparented to this
layout->addWidget(new QPushButton("bar")); // the button is reparented to this
}

Qt No matching function for call to mainWindow::connect()

I'm trying to connect a combo box value and a label such that when the combo box changes the label reflects that. I have googled my heart out trying to find an answer but, as of yet, nothing has worked; I still get the error:no matching function for call to mainWindow::connect(QComboBox*&, const char [38], QString*, const char [26])
I have tried QObject::connect, QWidget::connect and anything else dealing with Qt, but to no avail.
Creating a label that says the combo box value is not my final intention for the program. Rather, I wish to get it working with a simple label then change it to what I want it to display (thus the tempLabel).
mainwindow.h:
class MainWindow : public QMainWindow
{
public:
MainWindow();
private slots:
QString getClass(QComboBox *box);
};
mainwindow.cpp:
MainWindow::MainWindow()
{
QString qMathClassName;
QComboBox* mathClassCombo = new QComboBox;
QLabel* label = new QLabel(qMathClassName);
// omitting layout code...
connect(mathClassCombo, SIGNAL(currentIndexChanged(const QString &)),
&qMathClassName, SLOT(getClass(mathClassCombo)));
}
QString MainWindow::getClass(QComboBox *box)
{
return box->currentText();
}
Any help would be greatly appreciated!
You are connecting a signal to a slot with a different signature. You have to change your slot to something like
getClass(const QString &)
to match currentIndexChanged signal.
I think you need to read Qt's signals and slots documentation. Again, if you've already done so. Pay special attention to their examples.
I think that you had these misconceptions about Qt in C++:
That QLabel takes a reference to a QString, and that it will update its text when that string changes. It won't. QLabel will display the value of the string when you give it the string. That is the only time it will update.
That objects constructed on the stack will not be destroyed at the end of the function. They will not. At the end of the constructor, qMathClassName will be destroyed and any reference to it will become invalid. Thus, you'd not want to make a connection to it, even if you could.
That the third argument of QObject::connect is a pointer to a place to put the return value for the slot. It's not. The third argument is a pointer to the QObject on which to call the slot. The return value of a slot is unused for any calls made to it via QObject::connect.
That you can bind values to slots in your connection. Unfortunately not. Within the SLOT macro, you must put the function signature of the slot. You may not reference any variables. The arguments section must have only class names. That is SLOT(getClass(QComboBox*)), not SLOT(getClass(mathClassCombo)).
The simplest way to ensure the contents of a combo box are displayed in a label are this:
QComboBox* mathClassCombo = new QComboBox;
QLabel* tempLabel = new QLabel;
connect(mathClassCombo, SIGNAL(currentIndexChanged(const QString&)),
tempLabel, SLOT(setText(const QString&)));
If you want to do something more complicated, I recommend just making a slot on your window that can handle those complications. For example:
mainwindow.h:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow();
private slots:
void updateLabelText(const QString& className);
private:
QComboBox* mathClassCombo;
QLabel* tempLabel;
}
mainwindow.cpp:
MainWindow::MainWindow()
{
mathClassCombo = new QComboBox;
tempLabel = new QLabel;
// omitting layout code...
connect(mathClassCombo, SIGNAL(currentIndexChanged(const QString&)),
this, SLOT(updateLabelText(const QString&)));
}
void MainWindow::updateLabelText(const QString& className)
{
QString newLabelString = className + " is the best class ever!";
tempLabel->setCurrentText(newLabelString);
}

QtMenubar Call Slot

This is inside a Menu class. The problem is addAction. This works, but there is no connection to slot:
QMenu* menu2 = new QMenu("Test");
menu2->addAction("Test");
When I do this:
QMenu* menu2 = new QMenu("Test");
menu2->addAction("Test", Menu, test);
I get compiler error: "error: expected primary-expression before ',' token"
I mean to call the test() function in the Menu class. What am I doing wrong?
Well, the error comes from passing Menu as an argument. You say Menu is a class, and classes are not expressions on themselves.
If you need to call test on an instance of Menu, where Menu is not a derivate from QObject (ie. no slots available), then you can just create a slot in the widget that contains the QMenu itself (probably a QMainWindow), and implement the call in there!
Edit: to add an example.
class MainWindow : public QMainWindow {
Q_OBJECT
// Usual declarations...
private slots:
void myCustomSlot();
};
Now, say that you're populating the main window inside its constructor:
MainWindow::MainWindow(...) {
// Some initialization code
QMenu *menu2 = new QMenu("Test");
menu2->addAction("Test", this, SLOT(myCustomSlot));
// Some more initialization code
}
// ...
void MainWindow::myCustomSlot() {
instanceOfMenu->test();
}
Of course, if you're creating the menu outside that class, you'd need to make the slot public, but that's another issue