Qt: Widget method addButtons() does NOT work as needed [duplicate] - c++

This question already has answers here:
Unable to delete the widgets in sub-layout of a layout in Qt
(3 answers)
Closed 6 years ago.
I am using Qt5 (beginner) on Windows7.
In the main window of my app I want to display and remove some push-buttons.
widget = new ButtonWidget(ui->frame); // frame is a QScrollArea
connect(ui->addBtns, SIGNAL(clicked()), widget, SLOT(addButtons()));
connect(ui->deleteBtns, SIGNAL(clicked()), widget, SLOT(deleteButtons()));
And the ButtonWidget class is here:
ButtonWidget::ButtonWidget(QWidget * parent) : QWidget(parent)
{
//addButtons();
}
void ButtonWidget::addButtons()
{
QStringList texts{"1\nok", "2\nok", "3\nok", "4\nok", "5\nok", "6\nok"};
gridLayout = new QGridLayout;
for(int i = 0; i < texts.size(); i++)
{
QPushButton * button = new QPushButton(texts[i]);
gridLayout->addWidget(button, i / 5, i % 5);
}
setLayout(gridLayout);
}
// I'm not sure this method/function is ok... :(
void ButtonWidget::deleteButtons()
{
QLayoutItem * child;
while((child = gridLayout->takeAt(0)) != 0)
{
gridLayout->removeWidget(child->widget());
delete child->widget();
delete child;
}
delete gridLayout;
}
Problem is: when I click on add_buttons, I get all buttons displayed, but they are shrunk, tiny or something... :
OTOH... if I remove the comment from addButtons() call in the constructor (hence calling from within the constructor), the result is ok:
So, finally I have 2 questions:
1) How to fix the code to be able to add those buttons properly (when add_buttons is clicked)?
2) Is the deleteButtons() method ok?

First, I would recommend to do
gridLayout = new QGridLayout(this);
in the constructor. You don't need to delete all the grid, create it again and set in as a layout when you can just remove its content (with delete button for example) and fill it again afterwards.
EDIT : comment of Werner Erasmus : No need to set the layout. The fact that it has a parent widget implies that it sets itself up.
The problem is that is you do not modify addButtons() you will substitute the previous buttons without knowing where they go.
Also, try to give the QPushButton a parent :
new QPushButton(texts[i],this);
For your second point : Removing widgets from QGridLayout

EDIT:
After some more testing (without looking at the source code, but suspecting that it must be safe to delete button, else things would be brittle), I've implemented removeButtons as follows:
void ButtonWidget::deleteButtons()
{
while(myLayout->count())
{
delete myLayout->itemAt(0)->widget();
}
}
This works, and it proves that deleting a widget also removes the widget from it's parent, and layouts associated with the parent (by slots hooked up to when a child widget is deleted). The above code confirms this, as count(), which refers to number of layout items, decrease to zero (and those layout items are managed by the Layout). takeAt(x) is never called, and doesn't need to be - simply delete the widgets (buttons). Wholla!
ORIGINAL ANSWER
As mentioned in my other post, you only need to delete the buttons (it is removed from its parent automatically). However, if a widget was added to a layout, the parent of that layout becomes the parent of the widget, and an associated QLayoutItem is created that is managed by the layout itself. To the delete the buttons, the safest way is to take all the layout items (ownership the taker's responsibility), delete each items associated widget, and the delete each item. I'll try and find relevant references apart from the sources...
The following code works:
//ButtonWidget.h
#include <QWidget>
#include <QScrollArea>
#include <QHBoxLayout>
class ButtonWidget : public QScrollArea
{
Q_OBJECT
public:
ButtonWidget(QWidget *parent = 0);
~ButtonWidget();
void addButtons();
void deleteButtons();
private:
QHBoxLayout* myLayout;
};
//ButtonWidget.cpp
#include "ButtonWidget.h"
#include <QGridLayout>
#include <QPushButton>
#include <QLayoutItem>
ButtonWidget::ButtonWidget(QWidget * parent) :
QScrollArea(parent),
myLayout(new QHBoxLayout(this))
{
}
ButtonWidget::~ButtonWidget()
{
}
void ButtonWidget::addButtons()
{
QStringList texts{"1\nok", "2\nok", "3\nok", "4\nok", "5\nok", "6\nok"};
for(int i = 0; i < texts.size(); i++)
{
myLayout->addWidget(new QPushButton(texts[i]));
}
}
void ButtonWidget::deleteButtons()
{
QLayoutItem * child;
while((child = myLayout->takeAt(0)) != 0)
{
delete child->widget();
delete child;
}
}
#include "ButtonWidget.h"
#include <QApplication>
#include <QScrollArea>
#include <QPushButton>
#include <QGridLayout>
#include <QHBoxLayout>
#include <memory>
std::unique_ptr<QScrollArea> makeArea()
{
std::unique_ptr<QScrollArea> area(new QScrollArea);
auto layout = new QGridLayout(area.get());
auto addButton = new QPushButton("Add");
auto removeButton = new QPushButton("Remove");
layout->addWidget(addButton, 0, 0);
layout->addWidget(removeButton, 0, 1);
auto btnWidget = new ButtonWidget;
layout->addWidget(btnWidget,1,0,1,2);
QObject::connect(addButton, &QPushButton::clicked, [=]()
{
btnWidget->addButtons();
});
QObject::connect(removeButton, &QPushButton::clicked, [=]()
{
btnWidget->deleteButtons();
});
return move(area);
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
auto area = makeArea();
area->show();
return a.exec();
}
You need to enable c++11 in your config (.pro) to get the lambdas working.
CONFIG += c++11
I've used QHBoxLayout for your buttons, as it better models what you want. Although strictly not necessary, I'm returning unique_ptr from the makeArea in main, as it has not parent, I'm not sure whether it gets some parent because it is the first widget created, but unique_ptr shows intent.
NOTE:
Apparently the layout item is not the parent of the widget, but the widget associated with the layout itself is the parent of widgets belonging to its layout item.

Related

Is it possible to call a slot when any of the widgets in a dialog box emits a signal?

I am trying to create a configuration menu box for an application, and have used a QDialog box to display the options that the user can change. This box contains QComboBoxes and QLineEdits, but a lot of them (7 combo boxes and 12 line edits). There is a QPushButton in the bottom called "Apply Changes" that should get enabled only when any property in the box gets changed.
Do I have to link every signal from each widget with a slot to enable the button individually or is there a signal that the QDialog box itself emits when there is a change in its constituent widgets?
Right now I have this:
connect(Combo1,SIGNAL(activated(QString)),this,SLOT(fnEnable(QString)));
connect(Combo2,SIGNAL(activated(QString)),this,SLOT(fnEnable(QString)))
followed by 17 more lines of these connections.
void MyClass::fnEnable(QString)
{
ApplyButton->setEnabled(true); //It is initialised as false
}
I was wondering if there was a shorter way of doing this, maybe (like I mentioned before) a signal emitted by QDialog (I couldn't find one in the documentation)
I know that this does not speed up the program, as only the required connection is called, but it would make any further attempts at making more ambitious dialog boxes easier.
Actually there is no such signal, but one approach is to create a list of QComboBox, and make the connections with a for, for example:
QList <*QCombobox> l;
l<<combobox1<< combobox2<< ....;
for (auto combo: l) {
connect(combo, &QComboBox::activated, this, &MyClass::fnEnable);
}
The same would be done with QLineEdit.
You can iterate over an initializer list of widgets boxes and leverage C++11 to do all the boring work for you:
MyClass::MyClass(QWidget * parent) : QWidget(parent) {
auto const comboBoxes = {Combo1, Combo2, ... };
for (auto combo : comboBoxes)
connect(combo, &QComboBox::activates, this, &MyClass::fnEnable);
}
You can also automatically find all the combo boxes:
MyClass::MyClass(QWidget * parent) : QWidget(parent) {
ui.setupUi(this); // or other setup code
for (auto combo : findChildren<QComboBox*>(this))
connect(combo, &QComboBox::activated, this, &MyClass::fnEnable);
}
Or you can automatically attach to the user property's change signal. This will work on all controls that have the user property. The user property is the property of a control that contains the primary data the control is displaying.
void for_layout_widgets(QLayout * layout, const std::function<void(QWidget*)> & fun,
const std::function<bool(QWidget*)> & pred = +[](QWidget*){ return true; })
{
if (!layout) return;
for (int i = 0; i < layout->count(); ++i) {
auto item = layout->itemAt(i);
for_layout_widgets(item->layout(), fun, pred);
auto widget = item->widget();
if (widget && pred(widget)) fun(widget);
}
}
class MyClass : public QWidget {
Q_OBJECT
Q_SLOT void MyClass::fnEnable(); // must take no arguments
...
};
MyClass::MyClass(QWidget * parent) : QWidget(parent) {
// setup code here
auto slot = metaObject()->method(metaObject()->indexOfMethod("fnEnable()"));
Q_ASSERT(slot.isValid());
for_layout_widgets(layout(), [=](QWidget * widget){
auto mo = widget->metaObject();
auto user = mo->userProperty();
if (!user.isValid()) return;
auto notify = user.notifySignal();
if (!notify.isValid()) return;
connect(widget, notify, this, slot);
});
}
You can also keep the combo boxes in an array, by value. This minimizes the costs of indirect references and results in code that will take the least amount of memory possible and perform well:
class MyClass : public QWidget {
Q_OBJECT
QVBoxLayout m_layout{this};
std::array<QComboBox, 14> m_comboBoxes;
...
};
MyClass(QWidget * parent) : QWidget(parent) {
for (auto & combo : m_comboBoxes) {
m_layout.addWidget(&combo);
connect(&combo, &QComboBox::activates, this, &MyClass::fnEnable);
}
}

Deleting a widget from QTableView

I have a delete button(QPushButton) in the last column of each row of my table view. I am creating these push buttons and directly setting them in view. Since I have allocated memory dynamically I wish to free this memory but I haven't stored pointers of these buttons anywhere so I am trying to obtain the widget at the time of clean up and deleting them.
SDelegate* myDelegate;
myDelegate = new SDelegate();
STableModel* model = new STableModel(1, 7, this);
myWindow->tableView->setModel(model);
myWindow->tableView->setItemDelegate(myDelegate);
for(int i = 0; i < no_of_rows; ++i) {
QPushButton* deleteButton = new QPushButton();
myWindow->tableView->setIndexWidget(model->index(i, 6), deleteButton);
}
exec();
// Cleanup
for(int i = 0; i < no_of_rows; ++i) {
// code works fine on removing this particular section
QWidget* widget = myWindow->tableView->indexWidget(model->index(i, 6));
if (widget)
delete widget;
}
delete model;
delete myDelegate;
I am getting a crash in qt5cored.dll (Unhandled exception) and application is crashing in qcoreapplication.h at the following code:
#ifndef QT_NO_QOBJECT
inline bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
{ if (event) event->spont = false; return self ? self->notifyInternal(receiver, event) : false; }
While debugging there is no issue in deleting these widgets but code crashes afterwards at some other point. I am using QTableView and custom class for model which has inherited QAbstractTableModel.
There's a Qt bug that manifests as follow: if there are any index widgets, and you invoke setModel(nullptr) on the view, it'll crash in an assertion on visualRow != -1 in qtableview.cpp:1625 (in Qt 5.6.0). Presumably this bug could be triggered when the model is being removed in some other fashion too.
But I can't reproduce it by merely destroying the model instance. So I doubt that it's relevant here unless you get the same assertion failure.
Given the style of your code, it's more likely that you have a memory bug elsewhere. If you think that the code above is crashing, you should have a self-contained test case that demonstrates the crash. Is your model or delegate to blame? Would it crash using no delegate? Would it crash using a stock model?
Your code excerpt seems to be fine, if mostly unnecessary. You could allocate the delegate and the model locally. The buttons are owned by the view: as soon as the need for the buttons goes away, such as when the model changes the row count or goes away, they will get appropriately deleted. So you don't have to delete them yourself, it's safe but completely unnecessary.
Here's an example that demonstrates that in all cases, the buttons will get disposed when the model gets destroyed or the view gets destroyed, whichever comes first. Tracking object lifetime is super simple in Qt: keep a set of objects, and remove them from the set using a functor attached to the object's destroyed signal. In Qt 4 you'd use a helper class with a slot.
// https://github.com/KubaO/stackoverflown/tree/master/questions/model-indexwidget-del-38796375
#include <QtWidgets>
int main(int argc, char ** argv) {
QApplication app{argc, argv};
QSet<QObject*> live;
{
QDialog dialog;
QVBoxLayout layout{&dialog};
QTableView view;
QPushButton clear{"Clear"};
layout.addWidget(&view);
layout.addWidget(&clear);
QScopedPointer<QStringListModel> model{new QStringListModel{&dialog}};
model->setStringList(QStringList{"a", "b", "c"});
view.setModel(model.data());
for (int i = 0; i < model->rowCount(); ++i) {
auto deleteButton = new QPushButton;
view.setIndexWidget(model->index(i), deleteButton);
live.insert(deleteButton);
QObject::connect(deleteButton, &QObject::destroyed, [&](QObject* obj) {
live.remove(obj); });
}
QObject::connect(&clear, &QPushButton::clicked, [&]{ model.reset(); });
dialog.exec();
Q_ASSERT(model || live.isEmpty());
}
Q_ASSERT(live.isEmpty());
}
Check out QObject::deleteLater() , it usually helps with issues around deleting QObjects / QWidgets.

Qt RightClick on QListWidget Opens Contextmenu and Delete Item

I want to know how can I open a popup menu when I right click on the table items. In the popup menu some actions like add and delete should be given, which will create a new row or delete the selected row.
I am a new in the Qt world, so if anybody can give me the full details (with code if possible) then I will be really grateful towards him/her.
Thank you.
My goal: Only in the area of QListWidget and only if you click on an item, the menu with Delete will be opened.
Edit : Ok I solved the problem with the QListWidget and the menu. Now the following must be accomplished:
If you click on an item with the right mouse button, and then click Delete, then the item will be deleted.
My code:
void ProvideContextMenu(const QPoint &); // MainWindow.h
// In MainWindow.cpp
ui->listFiles->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->listFiles, SIGNAL(customContextMenuRequested(const QPoint &)),
this, SLOT(ProvideContextMenu(const QPoint &)));
void MainWindow::ProvideContextMenu(const QPoint &pos)
{
QPoint item = ui->listFiles->mapToGlobal(pos);
QMenu submenu;
submenu.addAction("ADD");
submenu.addAction("Delete");
QAction* rightClickItem = submenu.exec(item);
if (rightClickItem && rightClickItem->text().contains("Delete") )
{
ui->listFiles->takeItem(ui->listFiles->indexAt(pos).row());
}
}
Edit2 : Ok I solved the whole problem :D. I uploaded my code, if somebody needs something like that it can help him/her.
Firstly you need to create slot for opening context menu:
void showContextMenu(const QPoint&);
At constructor of your class, which used QListWidget, set context menu policy to custom and connect QListWidget::customContextMenuRequested(QPoint) signal and showContextMenu() slot like this:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
setupUi(this);
listWidget->setContextMenuPolicy(Qt::CustomContextMenu);
connect(listWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
}
Then need to realize context menu opening:
void MainWindow::showContextMenu(const QPoint &pos)
{
// Handle global position
QPoint globalPos = listWidget->mapToGlobal(pos);
// Create menu and insert some actions
QMenu myMenu;
myMenu.addAction("Insert", this, SLOT(addItem()));
myMenu.addAction("Erase", this, SLOT(eraseItem()));
// Show context menu at handling position
myMenu.exec(globalPos);
}
After this we need to realize slots for adding and removing QListWidget elements:
void MainWindow::eraseItem()
{
// If multiple selection is on, we need to erase all selected items
for (int i = 0; i < listWidget->selectedItems().size(); ++i) {
// Get curent item on selected row
QListWidgetItem *item = listWidget->takeItem(listWidget->currentRow());
// And remove it
delete item;
}
}
As you can see we iterate all selected items (for set multiple selection mode use setSelectionMode() method) and delete it by ourself, because docs says that
Items removed from a list widget will not be managed by Qt, and will
need to be deleted manually.
Adding some items is easier, my solution with static variable for different item caption looks like:
void MainWindow::addItem()
{
static int i = 0;
listWidget->addItem(QString::number(++i));
}
To simplify your code use Qt5 sytax for signals and slots. It eliminates the need to create intermediate slots.
I hope it helps to you.
It's much simpler than the accepted answer. You don't need to deal with creating a context menu or cursor positions or any of that. Instead of Qt::CustomContextMenu, use Qt::ActionsContextMenu and just add your actions directly to the widget:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
ui->setupUi(this);
// you can create the actions here, or in designer
auto actInsert = new QAction("Insert", this);
auto actDelete = new QAction("Delete", this);
// you can set up slot connections here or in designer
connect(actInsert, SIGNAL(triggered()), this, SLOT(addItem()));
connect(actDelete, SIGNAL(triggered()), this, SLOT(eraseItem()));
// and this will take care of everything else:
listWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
listWidget->addActions({ actInsert, actDelete });
}
void MainWindow::addItem () {
...; // add an item
}
void MainWindow::eraseItem () {
...; // erase an item
}
Everything above except addActions (I think) can also be done in Designer.
Alternatively, if you don't want to add actual slot functions for whatever reason, you can do everything in a lambda on connect, too:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
ui->setupUi(this);
// you can create the actions here, or in designer
auto actInsert = new QAction("Insert", this);
auto actDelete = new QAction("Delete", this);
connect(actInsert, &QAction::triggered, [=]() {
...; // add an item
});
connect(actDelete, &QAction::triggered, [=]() {
...; // erase an item
});
// and this will take care of everything else:
listWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
listWidget->addActions({ actInsert, actDelete });
}
The signal/slot option is more organized and flexible, but the lambda option is good for short highly specialized bits of code (or binding to functions that aren't slots).
This works for context menus on any widget. Also, the same QAction can be used on multiple widgets.

QTableView - Place pointer(highlight selection to the first row of list

I create own widget based on QTableView. It's something like file dialog (list). I want to act intuitively.
a) working with whole rows
b) indicator also worked with whole rows
c) using switched enter the lower level (subdirectory)
d) after run program or moving to a lower level cursor must be on the first row of the table (row 0)
And there is problem. I can not force the program to place the cursor on the first line.
I tried several methods, but none succeeded. setCurrentIndex. selectRow etc. Cursor is always somewhere else. Not highlighted, not on first line, but once it's on 10 position second on 4 position etc. It behaves unpredictably.
How can I do it?
Here my code is:
mFileBoxWidget::mFileBoxWidget(QWidget *parent) :
QTableView(parent)
,model(new QFileSystemModel())
{
this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
this->setShowGrid(false);
this->verticalHeader()->setVisible(false);
this->installEventFilter(this);
model->setReadOnly(true);
this->setModel(model);
this->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch );
this->setColumnWidth(1,70);
this->setColumnWidth(2,70);
this->setColumnWidth(3,110);
this->setRootIndex(model->setRootPath("C://"));
this->setSelectionMode(QAbstractItemView::SingleSelection);
this->setSelectionBehavior(QAbstractItemView::SelectRows);
//this->selectRow(0); //Does not work - goto first row
//this->setCurrentIndes(Index); //Does not work - goto index x=0 y=0
}
Thank you with advance for all your responses.
Solved!
The problem is that the model is asynchronous. So reads the data in another thread. When I tried to set the index to the first line, still basically did not exist. The solution is, of course, to wait for loading the thread. At this point signal directoryLoaded(QString) is send. As a result, it is necessary to wait for the signal, and only then set index.
connect(myModel, SIGNAL(directoryLoaded(QString)), this, SLOT(onLoaded()));
void mFileBoxWidget::onLoaded()
{
QModelIndex index = myModel->index(myModel->rootPath());
this->setCurrentIndex(index.child(0, index.column()));
}
You shouldn't name your member variable model. QTableView has function model(), the compiler thinks this->model is meant to be this->model(), therefore you get the error you mentioned.
This is untested code, but I think something like this should work:
QModelIndex firstRow = QTableView::model()->index(0, 0);
QTableView::selectionModel()->select(firstRow,
QItemSelectionModel::ClearAndSelect |
QItemSelectionModel::Rows );
EDIT: (2013-06-19 06:12:58 UTC)
A simple (and ugly) workaround that worked so far for me is triggering a call to m_tableView->selectRow(0); from a QTimer.
Here's the sample code:
Header:
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include <QWidget>
class QTableView;
class QFileSystemModel;
class MainWidget : public QWidget
{
Q_OBJECT
public:
MainWidget(QWidget *parent = 0);
~MainWidget();
private:
void layoutWidgets();
QFileSystemModel *m_model;
QTableView *m_tableView;
private slots:
void selectFirstRow();
// for debugging only
void selectionChanged();
};
#endif // MAINWIDGET_H
Implementation:
#include "mainwidget.h"
#include <QTableView>
#include <QHBoxLayout>
#include <QFileSystemModel>
#include <QHeaderView>
#include <QTimer>
MainWidget::MainWidget(QWidget *parent)
: QWidget(parent)
{
m_tableView = new QTableView(this);
m_model = new QFileSystemModel(this);
m_model->setReadOnly(true);
m_tableView->setModel(m_model);
m_tableView->setRootIndex(m_model->setRootPath(QDir::homePath()));
m_tableView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_tableView->setShowGrid(false);
m_tableView->verticalHeader()->setVisible(false);
m_tableView->setColumnWidth(1,70);
m_tableView->setColumnWidth(2,70);
m_tableView-> setColumnWidth(3,110);
m_tableView->setSelectionMode(QAbstractItemView::SingleSelection);
m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
//m_tableView->->setSectionResizeMode(0, QHeaderView::Stretch ); // Qt 5?
layoutWidgets();
connect(m_tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(selectionChanged()) );
// This works
QTimer::singleShot(1000, this, SLOT(selectFirstRow()));
// Direct invocation - doesn't works
// selectFirstRow();
}
void MainWidget::layoutWidgets()
{
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(m_tableView);
setLayout(mainLayout);
setFixedSize(500,500);
}
void MainWidget::selectFirstRow()
{
m_tableView->selectRow(0);
}
void MainWidget::selectionChanged()
{
qDebug("Selection changed");
}
MainWidget::~MainWidget()
{}
The weird thing is, if QTimer::singleShot() needs to be triggered with a delay of at least ~25 ms., otherwise it wouldn't work in my system.
Here's the alternative, subclassing QTableView:
#include "mytableview.h"
#include <QFileSystemModel>
#include <QHeaderView>
#include <QTimer>
MyTableView::MyTableView(QWidget *parent) : QTableView(parent)
{
QFileSystemModel *myModel = new QFileSystemModel;
setModel(myModel);
setRootIndex(myModel->setRootPath(QDir::homePath()));
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setShowGrid(false);
verticalHeader()->setVisible(false);
//installEventFilter(this);
myModel->setReadOnly(true);
//setSectionResizeMode(0, QHeaderView::Stretch ); // Qt 5
setColumnWidth(1,70);
setColumnWidth(2,70);
setColumnWidth(3,110);
setSelectionMode(QAbstractItemView::SingleSelection);
setSelectionBehavior(QAbstractItemView::SelectRows);
QTimer::singleShot(100, this, SLOT(selectFirstRow()));
connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(selectionChanged()));
}
void MyTableView::selectFirstRow()
{
// qDebug("Selecting first row");
// QModelIndex firstRow = QTableView::model()->index(0,0);
// if(firstRow.isValid()){
// selectionModel()->select(firstRow, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
// }else{
// qDebug("Invalid index");
// }
selectRow(0);
}
void MyTableView::selectionChanged()
{
qDebug("Selection changed.");
}

Delete created Qt elements and subclasses

I have a question about Qt. I am wondering how it is I should delete all the pointers I create. For example:
.h file
#ifndef MAINCALENDAR_H
#define MAINCALENDAR_H
#include<QWidget>
#include <QMap>
#include <QComboBox>
#include <QCalendarWidget>
#include <QRadioButton>
#include <QString>
#include <QtGui>
#include "selector.h"
#include <QInputDialog>
class mainCalendar : public QWidget
{
Q_OBJECT
public:
mainCalendar(QWidget * parent = 0);
~mainCalendar();
void showAppointments();
public slots:
void showLCFunc() {select->getTod()->getIntf()->getListClass().orderListChronologic(); printer * test = new printer; test->setList(TodFace->getList()); test->show();}
void showLPFunc() {select->getTod()->getIntf()->getListClass().orderListByPriority(); printer * test = new printer; test->setList(TodFace->getList()); test->show();}
void loadFile() {QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),"", tr("M-Calendar Files (*.mca)"));}
void saveFile() {QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),"", tr("M-Calendar Files (*.mca)"));qDebug() << fileName << endl;}
void intshow();
void updater() {if (!Interface->isHidden()) { app->setList(Interface->getList()); app->Beta_update(1,calendar->selectedDate()); }
else if (!TodFace->isHidden()) {tod->setList(TodFace->getList()); tod->Beta_update(1,calendar->selectedDate());} }
private:
QPushButton *showLC;
QPushButton *showLP;
QPushButton *searchButton;
QPushButton *updateButton;
QPushButton *saveButton;
QPushButton *loadButton;
QLabel *instructions;
QPushButton *backButton;
QPushButton *taskButton;
interface * Interface;
todFace * TodFace;
showTod * tod;
showApp * app;
QCalendarWidget *calendar;
QGridLayout *mainLayout;
Selector * select;
};
#endif // MAINCALENDAR_H
.cpp file:
#include "maincalendar.h"
mainCalendar::mainCalendar(QWidget *parent)
: QWidget(parent)
{
QHBoxLayout * footButtons = new QHBoxLayout;
showLC = new QPushButton(tr("'to-do' (chrono)"));
showLP = new QPushButton(tr("'to-do' (priority)"));
searchButton = new QPushButton(tr("&Search"));
saveButton = new QPushButton(tr("&Save calendar"));
loadButton = new QPushButton(tr("&Load Calendar"));
updateButton = new QPushButton(tr("Update"));
footButtons->addWidget(searchButton);
footButtons->addWidget(saveButton);
footButtons->addWidget(loadButton);
footButtons->addWidget(showLC);
footButtons->addWidget(showLP);
instructions = new QLabel(tr("To view or add data, double-click date in calendar"));
calendar = new QCalendarWidget;
calendar->setGridVisible(true);
calendar->setMinimumDate(QDate(2012, 1, 1));
calendar->setMaximumDate(QDate(2016,12,31));
backButton = new QPushButton(tr("&Back to calendar"));
select = new Selector(0,calendar,instructions,backButton, updateButton);
tod = select->getTod();
TodFace = tod->getIntf();
TodFace->hide();
TodFace->setCalendar(calendar);
tod->hide();
app = select->getApp();
Interface = app->getIntf();
Interface->hide();
Interface->setCalendar(calendar);
app->hide();
backButton->hide();
updateButton->hide();
connect(showLC,SIGNAL(clicked()),this,SLOT(showLCFunc()));
connect(showLP,SIGNAL(clicked()),this,SLOT(showLPFunc()));
connect(updateButton, SIGNAL(clicked()), this, SLOT(updater()));
connect(backButton, SIGNAL(clicked()), this, SLOT(intshow()));
connect(loadButton,SIGNAL(clicked()),this,SLOT(loadFile()));
connect(saveButton,SIGNAL(clicked()),this,SLOT(saveFile()));
connect(calendar, SIGNAL(activated(QDate)), this, SLOT(intshow()));
mainLayout = new QGridLayout;
this->setMinimumHeight(800);
this->setMinimumWidth(1000);
mainLayout->setColumnMinimumWidth(0,500);
mainLayout->addWidget(calendar,0,0);
mainLayout->addWidget(app,1,0);
mainLayout->addWidget(Interface,1,2);
mainLayout->addWidget(tod,1,0);
mainLayout->addWidget(TodFace,1,2);
mainLayout->addWidget(backButton,0,0,Qt::AlignTop);
mainLayout->addLayout(footButtons,2,0,Qt::AlignLeading);
mainLayout->addWidget(instructions,2,0,Qt::AlignTrailing);
mainLayout->addWidget(updateButton,2,2,Qt::AlignRight);
setLayout(mainLayout);
setWindowTitle(tr("M-Calendar"));
}
mainCalendar::~mainCalendar()
{
}
void mainCalendar::intshow()
{
if (Interface->isHidden()&&TodFace->isHidden())
{
select->setDate(calendar->selectedDate());
select->show();
Interface->setdate(calendar->selectedDate());
TodFace->setdate(calendar->selectedDate());
} else
{
backButton->hide();
updateButton->hide();
Interface->hide();
app->close();
TodFace->hide();
tod->close();
calendar->show();
instructions->show();
}
}
I am stuck here. Am I supposed to do to delete all the pointers (QPushbutton, etc) and subclasses so no memory leaks occur?
Short answer: No, you don't have to explicitly delete them.
In Qt, the QObjects are organized in object trees. The parent-child relationships of the various widgets also implies that the parent takes ownership of the child widgets.
As a result you do not have to explicitly delete them when your application finishes. Each parent will take care of the cleanup of its own children. Only when you create a (pointer to) a widget/object which has no parent, you will need to explicitly delete it.
via addWidget you create a parent-child-relationship between these to widgets.
Any parentless widget has to be destroyed manually, which in turn free its children recursivly.
As long as your widget has a parent, and that is either freed manually or child to another Widget, no manual free is required.
Same accounts for QObject derived classes.