Saving QTableView state
-
If a user has moved/resized the table columns, I want to save that when the table view is closed
-
Is that accomplished with e.g.
QSettings settings;
settings.setValue("windowState", saveState());
for the table view?
- Is it possible to do this without sub-classing QTableView to add a closeEvent handler? Can I do this in the owning widget's closeEvent() or has the TableView gone at that point?
Thanks
David -
-
The final code to save the state of the Horizontal header in the QMainWindow::closeEvent handler reads like this:
QSettings settings; settings.setValue("geometry", saveGeometry()); settings.setValue("windowState", saveState()); QTableView* tableView = this->findChild<QTableView*>("tableView"); settings.setValue("Dialogs/StackingDlg/TableView/HorizontalHeader/windowState", tableView->horizontalHeader()->saveState());it relies on the tableView object being named but that is normally true.
David -
If a user has moved/resized the table columns, I want to save that when the table view is closed
-
Is that accomplished with e.g.
QSettings settings;
settings.setValue("windowState", saveState());
for the table view?
- Is it possible to do this without sub-classing QTableView to add a closeEvent handler? Can I do this in the owning widget's closeEvent() or has the TableView gone at that point?
Thanks
David@Perdrix
What do you think yoursaveState()is a member of? YourQTableView?? It is a method inQMainWindow:Saves the current state of this mainwindow's toolbars and dockwidgets. This includes the corner settings which can be set with setCorner().
There is QWidget::saveGeometry(), but:
Saves the current geometry and state for top-level widgets.
Your
QTableViewperhaps is not a "top-level" widget, and I think this would only save geometry, nothing about any columns.Ah, I see there is a QHeaderView::saveState(). I would assume this is what you want.
Yes you should be able to call this from, say, "the owning widget's closeEvent() ". That is delivered before the widget is closed, upon a request to close, because you can e.g. choose to ignore the event and the widget will not close. The example at https://doc.qt.io/qt-6/qtwidgets-mainwindows-application-example.html#close-event-handler is often used as the place to save settings.
-
-
I sub-classed QTableView:
#include <QTableView> class QCloseEvent; class QContextMenuEvent; namespace DSS { class TableView : public QTableView { typedef QTableView Inherited; Q_OBJECT public: explicit TableView(QWidget* parent = nullptr); ~TableView() noexcept { } protected: void closeEvent([[maybe_unused]] QCloseEvent* e) override; void contextMenuEvent([[maybe_unused]] QContextMenuEvent* event) override; }; }and in the cpp file:
TableView::TableView(QWidget* parent) : QTableView{ parent } { QSettings settings; horizontalHeader()->restoreState( settings.value("Dialogs/StackingDlg/TableView/HorizontalHeader/windowState").toByteArray()); } void TableView::closeEvent([[maybe_unused]] QCloseEvent* e) { QSettings settings; settings.setValue("Dialogs/StackingDlg/TableView/HorizontalHeader/windowState", horizontalHeader()->saveState()); } void TableView::contextMenuEvent([[maybe_unused]] QContextMenuEvent* e) { QMenu menu(this); menu.exec(e->globalPos()); }But the closeEvent mf was not called (placed a breakpoint in there)??? (The ctor was invoked)
What am I doing wrong?
Thanks
David -
Hi,
Your error is to assume that all widgets get a close event. That's not the case. It's the case for the top level widget but not for its children.
Also, the saving and loading of setting should rather be handled by the widget that manages it.
-
So does that mean only the QMainWindow gets a closeEvent, or that if the TableView is a child of a Widget called StackingDlg, that it will get a closeEvent() ?
In this case the StackingDlg is someway down the hierarchy from QMainWindow:
widget = new QWidget(this); widget->setObjectName("centralWidget"); setCentralWidget(widget); QHBoxLayout* horizontalLayout{ new QHBoxLayout(widget) }; widget->setLayout(horizontalLayout); ZTRACE_RUNTIME("Creating Horizontal Splitter"); splitter = new QSplitter(Qt::Horizontal, widget); splitter->setObjectName("splitter"); ZTRACE_RUNTIME("Creating Explorer Bar (Left Panel)"); explorerBar = new ExplorerBar(widget); explorerBar->setObjectName("explorerBar"); splitter->addWidget(explorerBar); ZTRACE_RUNTIME("Creating stackedWidget"); stackedWidget = new QStackedWidget(splitter); stackedWidget->setObjectName("stackedWidget"); splitter->addWidget(stackedWidget); ZTRACE_RUNTIME("Creating Stacking Panel"); stackingDlg = new DSS::StackingDlg(widget); stackingDlg->setObjectName("stackingDlg"); ZTRACE_RUNTIME("Adding Stacking Panel to stackedWidget"); stackedWidget->addWidget(stackingDlg); winHost = new QWinHost(stackedWidget); winHost->setObjectName("winHost"); stackedWidget->addWidget(winHost); ZTRACE_RUNTIME("Creating Processing Panel"); BOOL result = processingDlg.Create(IDD_PROCESSING); //processingDlg.OnInitDialog(); HWND hwnd{ processingDlg.GetSafeHwnd() }; Q_ASSERT(NULL != hwnd); winHost->setWindow(hwnd); splitter->setStretchFactor(1, 1); // Want Stacking part to take any spare space. horizontalLayout->addWidget(splitter); -
I sub-classed QTableView:
#include <QTableView> class QCloseEvent; class QContextMenuEvent; namespace DSS { class TableView : public QTableView { typedef QTableView Inherited; Q_OBJECT public: explicit TableView(QWidget* parent = nullptr); ~TableView() noexcept { } protected: void closeEvent([[maybe_unused]] QCloseEvent* e) override; void contextMenuEvent([[maybe_unused]] QContextMenuEvent* event) override; }; }and in the cpp file:
TableView::TableView(QWidget* parent) : QTableView{ parent } { QSettings settings; horizontalHeader()->restoreState( settings.value("Dialogs/StackingDlg/TableView/HorizontalHeader/windowState").toByteArray()); } void TableView::closeEvent([[maybe_unused]] QCloseEvent* e) { QSettings settings; settings.setValue("Dialogs/StackingDlg/TableView/HorizontalHeader/windowState", horizontalHeader()->saveState()); } void TableView::contextMenuEvent([[maybe_unused]] QContextMenuEvent* e) { QMenu menu(this); menu.exec(e->globalPos()); }But the closeEvent mf was not called (placed a breakpoint in there)??? (The ctor was invoked)
What am I doing wrong?
Thanks
David@Perdrix said in Saving QTableView state:
I sub-classed QTableView:
No need to subclass, neither for save/restore state, nor for context menu.
As @SGaist said, do the all thing in the top level widget.
showEvent and closeEvent for the state,
and use customContextMenuRequested signal, see setContextMenuPolicy(). -
Hmmm the closeEvent() of StackingDlg also doesn't get triggered.
It's a royal mess to access the tableView from the closeEvent of the QMainWindow.
Would the destroy() mf for StackingDlg (which owns the QTableView) be a good location to save the horizontal header state for the table view?UPDATE! Arrgh QWidget::destroy() isn't virtual so I can't use that!
So what can I use simply to detect the one of my Widgets is going away.
PS setting the context menu policy for the table view to Qt::CustomContextMenu and having:
void StackingDlg::on_tableView_customContextMenuRequested(const QPoint& pos)is a really nice way to handle the popup menu!
Thanks
David -
Hmmm the closeEvent() of StackingDlg also doesn't get triggered.
It's a royal mess to access the tableView from the closeEvent of the QMainWindow.
Would the destroy() mf for StackingDlg (which owns the QTableView) be a good location to save the horizontal header state for the table view?UPDATE! Arrgh QWidget::destroy() isn't virtual so I can't use that!
So what can I use simply to detect the one of my Widgets is going away.
PS setting the context menu policy for the table view to Qt::CustomContextMenu and having:
void StackingDlg::on_tableView_customContextMenuRequested(const QPoint& pos)is a really nice way to handle the popup menu!
Thanks
David@Perdrix said in Saving QTableView state:
It's a royal mess to access the tableView from the closeEvent of the QMainWindow.
Why ?
stackingDlg = new DSS::StackingDlg(widget); stackingDlg->setObjectName("stackingDlg");stackingDlg not a variable of your mainWindow ?
you can even retrieve it by its name ;) -
The final code to save the state of the Horizontal header in the QMainWindow::closeEvent handler reads like this:
QSettings settings; settings.setValue("geometry", saveGeometry()); settings.setValue("windowState", saveState()); QTableView* tableView = this->findChild<QTableView*>("tableView"); settings.setValue("Dialogs/StackingDlg/TableView/HorizontalHeader/windowState", tableView->horizontalHeader()->saveState());it relies on the tableView object being named but that is normally true.
David -
The final code to save the state of the Horizontal header in the QMainWindow::closeEvent handler reads like this:
QSettings settings; settings.setValue("geometry", saveGeometry()); settings.setValue("windowState", saveState()); QTableView* tableView = this->findChild<QTableView*>("tableView"); settings.setValue("Dialogs/StackingDlg/TableView/HorizontalHeader/windowState", tableView->horizontalHeader()->saveState());it relies on the tableView object being named but that is normally true.
David@Perdrix said in Saving QTableView state:
it relies on the tableView object being named but is normally true.
Is this a statement about what you are "happy" with as your behaviour, or something you might like improved? Just a couple of comments:
-
IF you have only one table view then (are you aware that) you can omit the object name and just go
this->findChild<QTableView*>(), so you do not have to set any object name on it? -
If you have more than one, which one are you looking for to be saved under
Dialogs/StackingDlg/TableView/HorizontalHeader/windowState? -
Instead of relying on the object name, you might consider adding a property to the/those table view(s) to indicate this is/should be "saved", and search among all table views for that/those which have this property.
-
Of course, you could also subclass the
QTableViewto, say, aQSaveableTableViewand search for that class.
-
-
@Perdrix said in Saving QTableView state:
it relies on the tableView object being named but is normally true.
Is this a statement about what you are "happy" with as your behaviour, or something you might like improved? Just a couple of comments:
-
IF you have only one table view then (are you aware that) you can omit the object name and just go
this->findChild<QTableView*>(), so you do not have to set any object name on it? -
If you have more than one, which one are you looking for to be saved under
Dialogs/StackingDlg/TableView/HorizontalHeader/windowState? -
Instead of relying on the object name, you might consider adding a property to the/those table view(s) to indicate this is/should be "saved", and search among all table views for that/those which have this property.
-
Of course, you could also subclass the
QTableViewto, say, aQSaveableTableViewand search for that class.
@JonB said in Saving QTableView state:
Is this a statement about what you are "happy" with
Yes, I am happy with the normal default behaviour.
I wasn't aware of all the stuff in your reply, but all are useful bits of information.
I only have one table view to worry about :)
Thanks
D. -