Is there any way to save the order of columns? - c++

I currently have a tableview attached to a class that is derived from QSortFilterProxyModel. Now I wanted to know if there is any way by which I can store the order of columns since the users tend to move the columns back and forth. Also is there any signal that is emitted when a user changes the order of the columns.I search this but I cannot find anything that might tel me when a column is moved around and how i can save the tablew columns orders.
Any suggetsions would be appreciated

You need to obtain a QHeaderView object using QTableView::horizontalHeader. You can use QHeaderView::saveState and QHeaderView::restoreState to save state of columns. You can use QHeaderView::sectionMoved signal to detect column moving.

The reason why you cannot find the relevant signal in the documentation because you are checking an about 6-7 years old documentation. That is, it is Qt 4.1. The signal in question was added to Qt in version 4.6.
QAbstractItemModel has this signal recently:
void QAbstractItemModel::columnsMoved(const QModelIndex & sourceParent, int sourceStart, int sourceEnd, const QModelIndex & destinationParent, int destinationColumn) [signal]
This signal is emitted after columns have been moved within the model. The items between sourceStart and sourceEnd inclusive, under the given sourceParent item have been moved to destinationParent starting at the column destinationColumn.
Note: Components connected to this signal use it to adapt to changes in the model's dimensions. It can only be emitted by the QAbstractItemModel implementation, and cannot be explicitly emitted in subclass code.
This function was introduced in QtCore 4.6.
This looks like what you are looking for. See the documentation for further details:
http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#columnsMoved
Also, do not forget the fact that you will actually need QAbstractTableModel in the end of the day.
If you really wish, you could catch this signal as well:
void QHeaderView::sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex) [signal]
This signal is emitted when a section is moved. The section's logical index is specified by logicalIndex, the old index by oldVisualIndex, and the new index position by newVisualIndex.
Please refer to the documentation for further details:
http://qt-project.org/doc/qt-5.1/qtwidgets/qheaderview.html#sectionMoved

Related

QT MVC pattern not updating view - specific SimpleTreeModel example

I have had problems in my own code to get the views to update after model data is updated.
I then took the SimpleTree example from QT and added a timer in TreeModel to change the data after 10s and then invoke the same set data function used in the constructor to update the model. The code is below for the slot that executes on the timer timeout. No matter what I try, the view does not update. The setDate and emit dataChanged were some attempts.
In my own code, I have a XML-RPC call updating the data, but considering I dont even get the simpleTreeModel to work, I suppose that would be a long shot.
Is there something fundamental that I am missing here?
void TreeModel::slotTimeout(void)
{
QStringList tmp;
tmp << "qaz";
tmp << "wsx";
tmp << "edc";
setupModelData(tmp,rootItem);
setData(QModelIndex(),QModelIndex());
emit dataChanged(QModelIndex(), QModelIndex());
qDebug() << "Timer update";
}
The SimpleTreeModel example is for static models only. It lacks the implementation of the required QAbstractItemModel functions to update the model.
Have a look on the detailed description of the models documentation in order to see what should be implemented.
The problem is, that the required methods are implemented as empty methods by default so you will not get any error messages if something is missing. It will just not work.
In addition it‘s a bit tricky to do the necessary data changed emits.
Within the setData method you have to emit dataChanged().
Within the also necessary insertRows you have to call the methods beginInsertRows(...) and endInsertRows() in order to get the required signals emitted.
A first approach toward the MV paradigma is to use the QStandardItemModel. It provides all the necessary implementation if a QStandardItem is sufficient what it usually will be.

How to determine which widget triggered the slot function?

I have an array of 10 objects, each having 8 parameters, all represented in GUI. I'd rather not have 80 slots defined; I'd much prefer to have 1 slot handling all the GUI triggered changes:
// Connect 10 Term objects
for( int n = 0; n < m_MaxTerms; ++n )
{
// Connect several checkboxes for the nth Term item
connect(m_Term[n].m_CD.GetData(), SIGNAL(clicked(bool)), this, SLOT(UpdateTerm()));
// Connect several edit fields for the nth Term item
connect(m_Term[n].m_Volume.GetData(), SIGNAL(editingFinished()), this, SLOT(UpdateTerm()));
...
}
When UpdateTerm() is called I need to update the corresponding data based on the changes in the widget that triggered it. But how could I tell, from within UpdateTerm(), what widget triggered it? One way to solve the problem is to update data from all widgets when the slot is triggered by any of them. However, this is very inefficient; updating only the changed item would be much preferred.
Thus the question: is it possible from the slot function to determine which of the widgets triggered it? What would be the cleanest method of doing so?
You can use the QObject::sender() function to determine which object emitted the signal. This function is documented here.

Undo for individual cells of QTableWidget in Qt?

I have a QTableWidget in my Main Window class.
I am unable to find a functionality which will undo the text change for the specified cell.
What I want to do is:
void myCellUndoFunc(int row, int col)
{
table->item(row, col)->undo(); //table is my QTableWidget
}
The problem is that there is no such undo().
So my question is, can there be a workaround for this problem using maybe some foo-doo combination of SIGNAL's & SLOT's?
Thanks!
PS: Please do not suggest to use Model/View framework because I have used QTableWidget extensively in my application. Sorry for the same.
Maybe you should use the
void QTableWidgetItem::setData ( int role, const QVariant & value ) [virtual]
using the Qt::UserRole you are able to specify the last value. In your method u can access the previously set value with the data()-Method. The only thing you have to do is always keep the old value up-to-date.
Before you set the new value of the QTableWidgetItem
tw->setData(Qt::UserRole, tw->text())
and on undo u could than retrieve the data with
tw->setText(tw->data(Qt::UserRole).toString())
where "tw" is the current QTableWidgetItem using the contextmenu-event, the clicked-event or whatever u want. You could also subclass the QTableWidgetItem and handle this whole thing internally in your class, creating an undo()-method, storing the old value, etc.

Qt QTreeView not updating when adding to model

The source code relating to this question is available on my public Git repository on BitBucket.
I'm trying to dynamically add some items to a QTreeView model using the following code in mainwindow.cpp:
if(dlg->exec() == QDialog::Accepted) {
QList<QVariant> qList;
qList << item.name << "1111 0000" << "0x00";
HidDescriptorTreeItem *item1 = new HidDescriptorTreeItem(qList, hidDescriptorTreeModel->root());
hidDescriptorTreeModel->root()->appendChild(item1);
}
This works when run from within my MainWindow constructor, just after ui->setupUi(this), but I need this to run from within an event filter, but the same code doesn't get the QTreeView updating. When I set a breakpoint at mainwindow.cpp:70 and step through the next few lines, I can see the data is being added to the Model, but I need the QTreeView to refresh.
I understand this is done by emitting dataChanged(), but not really sure how to do this. The signal signature for the dataChanged signal looks as follows:
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = QVector<int>());
so I need to come up with topLeft and bottomRight QModelIndex instances. How do I build/obtain these from item1 in the above snippet?
Also, where does beginInsertRows() and endInsertRows() come into view with this, should I be calling these functions?
From QAbstractItemModel documentation:
void QAbstractItemModel::beginInsertRows ( const QModelIndex & parent, int first, int last ) [protected]
Begins a row insertion operation.
When reimplementing insertRows() in a subclass, you must call this function before inserting data into the model's underlying data store.
The parent index corresponds to the parent into which the new rows are inserted; first and last are the row numbers that the new rows will have after they have been inserted.
The other protected functions say similar things.
And insertRows() says:
If you implement your own model, you can reimplement this function if
you want to support insertions. Alternatively, you can provide your
own API for altering the data. In either case, you will need to call
beginInsertRows() and endInsertRows() to notify other components that
the model has changed.
Take a look to QAbstractItemModel protected functions and signals
Views connect to those signals to know when model data changes and rearrange data inside. The functions emit the signals internally to make it easy for you to warn the view when it has happenned. But signals can only be emitted by abstract class.
Components connected to this signal use it to adapt to changes in the
model's dimensions. It can only be emitted by the QAbstractItemModel
implementation, and cannot be explicitly emitted in subclass code.
So you will have to stick to the methods.
Edit in answer to your comment:
Indeed, Items should have a reference to model and tell it about changes, check theses lines from QStandardItem:
void QStandardItem::emitDataChanged()
void QStandardItem::removeRows(int row, int count)
( Note, how, in second, it calls model's rowsAboutToBeRemoved() and rowsRemoved() )
Maybe you should try to use QStandardItem and QStandardItemModel.
Either direct or subclassing. It will hide a lot of ugly stuff.
There's also a less proper but much easier way to achieve this - emit layoutChanged() instead of dataChanged(). More info - https://stackoverflow.com/a/41536459/635693

Catch QTableWidgetItem check state change

I have one QTableWidget with some QTableWidgetsItems on it. Some items use checkboxes. I've added the checkboxes using the follow code:
QTableWidgetsItem->setCheckState(Qt::Checked);
I would like now to call some function when this checkbox state change. Using a signal for example.
What may be the easiest way to accomplish this?
The easiest way to do this is to capture signal(s) of QTableWidget with slot(s) in the class that contains QTableWidget. While it would seem that QTableWidget::itemActivated might be our best bet, it is uncertain whether or not this is emitted when the Qt::CheckState is equal to Qt::Checked. In addition, even if this was true, the signal would not provide you the capabilities of handling item unchecking which your application may need to do.
So, here is my proposed solution. Capture the QTableWidget::itemPressed and QTableWidget::itemClicked signals with slots defined in the class that contains the QTableWidget. As itemPressed should be called BEFORE the mouse button is released, and itemClicked should be called AFTER the mouse button is released, the Qt::CheckState for that QTableWidgetItem should only be set in between these two signal emissions. Thus, you can determine exactly when a QTableWidgetItem's checkState has changed with low memory overhead.
Here is an example of what these slots could look like:
void tableItemPressed(QTableWidgetItem * item)
{
// member variable used to keep track of the check state for a
// table widget item currently being pressed
m_pressedItemState = item->checkState();
}
void tableItemClicked(QTableWidgetItem * item)
{
// if check box has been clicked
if (m_pressedItemState != item->checkState())
{
// perform check logic here
}
}
And the signals/ slots would be connected as follows:
connect(m_tableWidget,SIGNAL(itemPressed(QTableWidgetItem *)),this,SLOT(tableItemPressed(QTableWidgetItem *)));
connect(m_tableWidget,SIGNAL(itemClicked(QTableWidgetItem *)),this,SLOT(tableItemClicked(QTableWidgetItem *)));
Where m_tableWidget is the QTableWidget * you associate with your table widget.