This example shows how to use a QListView to display a list of strings in a view where each row contains one item. Users can add, edit, and remove entries and the list will remain sorted. A string model is used to store the data and a proxy model is used to automatically sort the data.
#include <QtCore>
#include <QtGui>
class MainWindow : public QWidget
{
public:
MainWindow();
private:
QListView *m_listView;
QSortFilterProxyModel *m_proxyModel;
void addEntry();
void removeEntry();
};
This declaration for MainWindow declares a constructor and two data members. The two slot method declarations will be called to add and remove items in the view.
MainWindow::MainWindow()
{
setMinimumSize(500, 600);
QLabel *label = new QLabel();
label->setText("List View");
label->setAlignment(Qt::AlignHCenter);
QFont font = label->font();
font.setPointSize(11);
label->setFont(font);
QStringList data = { "apple", "watermelon", "strawberry",
"nectarine", "guava", "orange" };
QStringListModel *model = new QStringListModel(this);
model->setStringList(data);
m_proxyModel = new QSortFilterProxyModel(this);
m_proxyModel->setSourceModel(model);
m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
m_proxyModel->setDynamicSortFilter(true);
m_proxyModel->sort(0, Qt::AscendingOrder);
m_listView = new QListView();
m_listView->setModel(m_proxyModel);
m_listView->setEditTriggers(
QAbstractItemView::DoubleClicked);
QModelIndex index = m_proxyModel->index(0, 0);
m_listView->setCurrentIndex(index);
QPushButton *pb_plus = new QPushButton();
pb_plus->setIcon(QIcon("../plus.png");
QPushButton *pb_minus = new QPushButton();
pb_minus->setIcon(QIcon("../minus.png"));
QPushButton *close_PB = new QPushButton();
close_PB->setText("Close");
QHBoxLayout *layout = new QHBoxLayout();
layout->addStretch();
layout->addWidget(pb_plus);
layout->addSpacing(8);
layout->addWidget(pb_minus);
layout->addSpacing(30);
layout->addWidget(pb_close);
layout->addStretch();
QVBoxLayout *layout_main = new QVBoxLayout(this);
layout_main->setContentsMargins(20, 20, 20, 15);
layout_main->addWidget(label);
layout_main->addSpacing(10);
layout_main->addWidget(m_listView);
layout_main->addSpacing(10);
layout_main->addLayout(layout);
connect(pb_plus, &QAbstractButton::clicked,
this, &MainWindow::addEntry);
connect(pb_minus, &QAbstractButton::clicked,
this, &MainWindow::removeEntry);
connect(close_pb, &QPushButton::clicked,
this, &QWidget::close);
}
The first few lines of code set up a label which will appear above the frame of the view. The font size of this label is increased for readability. On line 13 a variable is declared and initialized to a list of strings.
Rather than having the view handle data directly a model is used to store and manage the list of strings. The pointer to a new QStringListModel is declared on line 16 and then our data is saved to the model.
A second model is used to sort the data. Line 19 initializes a pointer to a new QSortFilterProxyModel. The call to setSourceModel() tells the proxy model where to find the stored data. The remaining three calls define the case sensitivity, turn on sorting, and then indicate which column should be used for sorting. Columns are numbered starting with zero.
On line 25 a new QListView object is constructed. Even though the proxy model knows where to find the data, we need to call setModel() and pass a pointer to our QSortFilterProxyModel.
Lines 30 and 31 will select and highlight the first row of the view when it is first displayed.
To add and remove items from the view a common approach is to use push buttons with icons. The “plus” icon will be used for the add button and a “minus” icon will be displayed on the remove button.
Signal / Slot Connections
On lines 60 and 63 two connections are set up to call the addEntry() and removeEntry() slot methods when the corresponding push buttons are clicked.
void MainWindow::addEntry()
{
// turn sorting off
m_proxyModel->setDynamicSortFilter(false);
m_proxyModel->sort(-1);
int row = m_listView->currentIndex().row();
m_proxyModel->insertRows(row, 1);
// edit new element
QModelIndex index = m_proxyModel->index(row, 0);
m_listView->setCurrentIndex(index);
m_listView->edit(index);
// turn sorting on
m_proxyModel->setDynamicSortFilter(true);
m_proxyModel->sort(0, Qt::AscendingOrder);
}
void MainWindow::removeEntry()
{
m_proxyModel->removeRows(
m_listView->currentIndex().row(), 1);
}
The implementation for addEntry() shows a few lines of code before the row is physically added. It is customary to turn off sorting in the proxy model so the screen does not flicker and our edit occurs on the new item. On line 7 currentIndex() returns the model index for the current row. This is the only way to ask the proxy model for the integer row number. Rows are numbered starting with zero.
On line 8 the proxy model inserts one new row directly after the current row.
Before we can edit the new item we need to ask the proxy model for the current model index. This must be passed to the listView by calling setCurrentIndex() to keep the proxy model and the view in sync. On line 13 the UI changes slightly and a text edit with a cursor is displayed. This will allow the user to see where to enter the new data.
Lines 16 and 17 turn sorting back on so the new item will be in the correct sorted location.
The slot method removeEntry() only requires one line of code. Since we are using a proxy model, this is the model which is responsible for removing rows. The parameters to removeRows() are the current row number followed by how many rows to remove. To find the integer value for the current row we simply need to retrieve the current model index and then the query the model index for the row number.
Main Function
Since the source code for main() has not changed there is no need to show it again. Refer to example 3 or download the full source for this example.
Running the Example
To build and run this example use the same CMake build file and commands as we showed for the first example. The only suggested modification is on line 2 of the CMakeLists.txt file, change “example_1” to “example_22”.