Qt - kontrolka QListView

Autor podstrony: Krzysztof Zajączkowski

Stronę tą wyświetlono już: 4008 razy

Kontrolka QListView wizualnie przypomina wcześniej omawianą kontrolkę QListWidget, jednakże funkcjonuje ona na nieco innej zasadzie i umożliwia znacznie większą elastyczność sterowania jej funkcjonalnością i wyglądem. Niestety jest to obarczone nieco bardziej złożonym procesem obsługi tej kontrolki. W przykładzie, który mam zamiar pokazać wyeliminowałem konieczność tworzenia dodatkowych kontrolek do wprowadzania danych przez użytkownika do mojej listy. W kontrolce QListView po odpowiednim ustawieniu jej właściwości metodą setEditTriggers i zaznaczeniu myszką jednej z dostępnych pozycji można zmienić jej zawartość wpisując po prostu tekst. Klawisz enter zatwierdza zmianę. To samo można zrobić klikając dwukrotnie na danym elemencie listy i edytując zawarty w nim tekst.

Zanim przejdę do kodu warto zerknąć łaskawym okiem na wygląd samego graficznego interfejsu użytkownika, na który składa się tylko jedna kontrolka QListView, która zastąpi wszystkie dodatkowe przyciski i kontrolkę QLineEdit z poprzedniej strony.

Qt Creator - interfejs graficzny z kontrolką QListView
Rys. 1
Qt Creator - interfejs graficzny z kontrolką QListView

W kodzie obiekt klasy QListView nazwałem countriesList.

Kod programu w pliku mainwindow.h:

#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QStringListModel> #include <QDebug> #include <QMessageBox> #include <QShortcut> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT QStringListModel *countriesListModel; // model, który zostanie podpięty pod kontrolkę QListView QString country; // pomocnicza zmienna konieczna do przechowywania informacji o tekście zawartym w danym elemencie kontrolki przed jego modyfikacją QShortcut *shDelete; // skrót klawiaturowy do podpięcia slotu onDeleteItem public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private slots: // slot podpięty pod sygnał wysyłany, gdy tekst w elementach kontrolki został zmieniony void countryDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = QVector<int> ()); // slot podpięty pod sygnał wysyłany, gdy element w kontrolce został kliknięty dwa razy (co będzie oznaczało edycję tekstu kontrolki void on_countriesList_doubleClicked(const QModelIndex &index); // slot podpięty pod skrót klawiaturowy oznaczający usuwanie elementu zaznaczonego void onDeleteItem(); // slot podpięty pod sygnał wysyłany, gdy wcześniej zaznaczony na liście element został nadpisany poprzez rozpoczęcie wpisywania tekstu void on_countriesList_pressed(const QModelIndex &index); private: Ui::MainWindow *ui; }; #endif // MAINWINDOW_H

Kontrolka QListView do dodawania elementów listy wykorzystuje obiekt klasy QStringListModel, który po utworzeniu trzeba podpiąć pod obiekt klasy QListView wykorzystując jej metodę setModel. Wszystko to zostanie uczynione w konstruktorze klasy MainWindow w pliku mainwindow.cpp:

#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); countriesListModel = new QStringListModel(this); // tworzenie nowego obiektu modelu countriesListModel->setStringList( {"Polska", "Czechy", "Słowacja"} // dodaję parę początkowych pozycji ); ui->countriesList->setModel(countriesListModel); // podpinam model pod kontrolkę QListView ui->countriesList->setEditTriggers( QAbstractItemView::AnyKeyPressed | // edycja zaznaczonego elementu na dowolny znak tekstowy QAbstractItemView::DoubleClicked // edycja elementu po dwukrotnym kliknięciu lewym przyciskiem myszy ); int row = countriesListModel->rowCount(); // pobieram liczbę wierszy countriesListModel->insertRow(row); // wstawiam dodatkowy wiersz na końcu QModelIndex index = countriesListModel->index(row, 0); // pobieram obiekt wstawionego indeksu countriesListModel->setData(index, QVariant("*")); // i ustawiam jego tekst // podpięcie sygnału dataChanged obiektu countriesListModel pod slot countryDataChanged okna głównego QObject::connect(countriesListModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), this, SLOT(countryDataChanged(QModelIndex,QModelIndex,QVector<int>))); // tworzę skrót klawiaturowy podpięty pod kontrolkę QListView QShortcut* shDelete = new QShortcut(QKeySequence(Qt::Key_Delete), ui->countriesList); // podpinam sygnał obiektu shDelete pod slot onDeleteItem okna głównego connect(shDelete, SIGNAL(activated()), this, SLOT(onDeleteItem())); } MainWindow::~MainWindow() { delete ui; } void MainWindow::countryDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles){ if(topLeft != bottomRight) // jak więcej niż dwa elementy są edytowane to nie wykonuj dalszych działań return; int rowCount = countriesListModel->rowCount(); // pobieram liczbę wierszy // jeżeli tekst jest pusty if(topLeft.data().toString() == ""){ countriesListModel->setData(topLeft, country); // to przywracam pierwotne ustawienie return; } // wyszukuję elementy pasujące do wprowadzonego tekstu QModelIndexList finded = countriesListModel->match(countriesListModel->index(0, 0), Qt::DisplayRole, topLeft.data(), -1, Qt::MatchExactly); // jak znaleziono więcej niż jeden to znaczy, że taki element już istniał if(finded.size() > 1){ QMessageBox::warning(this, "Uwaga!", "Kraj o nazwie: " + topLeft.data().toString() + " już jest na liście"); countriesListModel->setData(topLeft, country); }else if(topLeft.row() == rowCount - 1){ // jeżeli edytowany był ostatni element if(topLeft.data().toString() != "*"){ // i tekst nie jest "*" to countriesListModel->insertRow(rowCount); // dodaję nową pozycję QModelIndex index = countriesListModel->index(rowCount, 0); // pobieram jej obiekt countriesListModel->setData(index, QVariant("*")); // i ustawiam tekst na "*" } } } void MainWindow::on_countriesList_doubleClicked(const QModelIndex &index) { country = index.data().toString(); // zapamiętywanie początkowej wartości edytowanego indeksu } void MainWindow::onDeleteItem(){ int row = ui->countriesList->currentIndex().row(); // pobieram indeks if(row != countriesListModel->rowCount() - 1){ // jak nie jest równy ostatniemu elementowi to countriesListModel->removeRow(row); // usuwam wiersz } } void MainWindow::on_countriesList_pressed(const QModelIndex &index) { country = index.data().toString(); // zapamiętywanie początkowej wartości edytowanego indeksu }
Strony powiązane
strony powiązane
  1. doc.qt.io/qt-5/qlistview.html#dataChanged - opis klasy QListView na stronie dokumentacji Qt