БлогNot. Qt: создаём список QListWidget и управляем его элементами

Qt: создаём список QListWidget и управляем его элементами

Просто чтобы не затерялся пример в недрах авторизованного мира, а вообще, в блоге уже есть несколько статей по спискам в Qt.

В Qt есть две группы встроенных списочных виджетов:

  • Model-based: рассчитаны на проектирование приложений, работающих с данными в парадигме MVC (см., например, здесь);
  • Item-based: основаны на прямой работе с элементами. Пока займёмся ими, как более понятными.

Создадим виджет с формой на основе класса QWidget как в предыдущих примерах.

С помощью стандартных средств компоновки подготовим форму со списком QListWidget и панелью кнопок для выполнения типовых действий над списком.

форма окна приложения
форма окна приложения

Можно было создать список и программно, например, в конструкторе виджета:

QListWidget *list = new QListWidget(this);

Элементы списка QListWidgetItem могут быть добавлены в список программно или же созданы без родительского элемента-списка и потом добавлены отдельно (выполним этот код по кнопке 1):

void Widget::on_pushButton_clicked() {
 new QListWidgetItem(tr("Элемент 1"),ui->listWidget);
 QString item2Title ("Элемент 2");
 QListWidgetItem *item2 = new QListWidgetItem;
 item2->setText(item2Title);
 item2->setIcon(QIcon(":/images/new.png"));
 item2->setToolTip("Всплывающая подсказка");
 item2->setStatusTip("Сообщение в строку статуса");
 item2->setWhatsThis("Подсказка \"Что это?\"");
 ui->listWidget->addItem(item2);
}

Из примера видно, что элемент легко снабдить дополнительными "украшениями", такими как иконка-пиктограмма и три вида подсказок. Конечно, чтобы все подсказки работали, нужно, чтобы были доступны соответствующие элементы окна (строка статуса, кнопка "What’s This?").

Иконка загружается из ресурсов проекта (см. файл application.qrc и подключение ресурсов в ListWidget1.pro: RESOURCES = application.qrc

По умолчанию элементы списка отсортированы в порядке их добавления. Легко отсортировать список и в алфавитном порядке (выполним по кнопке 2):

void Widget::on_pushButton_2_clicked() {
 static Qt::SortOrder order = Qt::AscendingOrder;
 ui->listWidget->sortItems(order);
 if (order == Qt::AscendingOrder) order = Qt::DescendingOrder;
 else order = Qt::AscendingOrder;
}

По кнопке 3 посмотрим основные возможности получения элемента списка в виде строки QString:

void Widget::on_pushButton_3_clicked() {
 QListWidgetItem *item = ui->listWidget->currentItem(); //получить текущий
 if (!item) {
  QMessageBox::information(this,tr("Не могу получить currentItem"),"NULL");
 }
 else {
  QMessageBox::information(this,tr("Получение currentItem"),item->text());
 }
 QListWidgetItem *item2 = ui->listWidget->item(ui->listWidget->currentRow()); 
  //получить по индексу
 if (!item2) {
  QMessageBox::information(this,tr("Не могу получить элемент по индексу"),"NULL");
 }
 else {
  QMessageBox::information(this,tr("Получение по индексу"),item->text());
 }
}

По кнопке 4 сделаем выбранный текущий элемент редактируемым:

void Widget::on_pushButton_4_clicked() {
 QListWidgetItem *item = ui->listWidget->currentItem();
 if (!item) {
  QMessageBox::information(this,tr("Ошибка"),tr("Не выбран элемент в списке"));
  return;
 }
 item->setFlags (item->flags () | Qt::ItemIsEditable);
}

Теперь его можно открыть двойным кликом для изменения. Чтобы проверять и, при необходимости, отменять сделанные пользователем изменения, можно отслеживать их с помощью сигнала QListWidget::itemChanged. Правда, есть 2 проблемы:

  • Любое другое изменение элемента в списке также вызовет этот сигнал, поэтому нужно заключить любой код, который изменяет элементы, в вызовы QObject::blockSignals, чтобы заблокировать нежелательные сигналы;
  • Нет способа получить предыдущий текст, вы можете получить только новый текст. Решением является либо сохранение всех данных списка в переменную, их использование и обновление при изменении, либо сохранение текста отредактированного элемента перед его редактированием, как мы и сделали.

void Widget::on_listWidget_itemChanged(QListWidgetItem *item) {
 if (!item) return;
 QString text = item->text();
 ui->listWidget->blockSignals(true);
 if (text.contains(QRegExp("^[\\w\\s]+$"))) item->setText(text);
  //Только алфафитно-цифровые или разделители
 else item->setText(this->previousText);
 ui->listWidget->blockSignals(false);
}

void Widget::on_listWidget_itemDoubleClicked(QListWidgetItem *item) {
 if (item) this->previousText = item->text();
}

Мы создали эти обработчики, нажав правой кнопкой мыши на списке и перейдя к соответствующим слотам, названия которых видны из названий функций.

Вообще же, если от виджета требуется нетипичное поведение, обычно целесообразней писать класс-потомок с нужными изменениями, чем пытаться перехватывать или модифицировать сигналы исходного виджета.

Кнопку 5 научим удалять элемент, это просто:

void Widget::on_pushButton_5_clicked() {
 QListWidgetItem *item = ui->listWidget->item(ui->listWidget->currentRow());
 if (!item) {
  QMessageBox::information(this,tr("Ошибка"),tr("Не выбран элемент в списке"));
  return;
 }
 ui->listWidget->takeItem (ui->listWidget->currentRow());
}

А кнопка 6 очистит весь список:

void Widget::on_pushButton_6_clicked() {
 ui->listWidget->clear();
}

Кнопка 7 будет переключать между режимами выделения одного и нескольких элементов в списке, выделение нескольких элементов по умолчанию допускается при зажатой клавише Ctrl:

void Widget::on_pushButton_7_clicked() {
 static QAbstractItemView::SelectionMode mode = QAbstractItemView::SingleSelection;
 if (mode == QAbstractItemView::SingleSelection) mode = QAbstractItemView::MultiSelection;
 else mode = QAbstractItemView::SingleSelection;
 ui->listWidget->setSelectionMode(mode);
}

Кнопка 8 покажет в отдельном окне диалога текст всех выбранных элементов, если таковые есть:

void Widget::on_pushButton_8_clicked() {
 QList <QListWidgetItem *> selected = ui->listWidget->selectedItems();
 if (selected.size()==0)  {
  QMessageBox::information(this,tr("Ошибка"),tr("Нет выбранных элементов"));
  return;
 }
 QString str("");
 foreach(QListWidgetItem *item, selected) {
  str += item->text() + "\n";
 }
 QMessageBox::information(this,tr("Выбранные элементы"),str);
}

Кнопка 9 сохранит список в текстовый файл с именем data.txt (только текст элементов), а кнопка 10 загрузит данные из файла:

void Widget::on_pushButton_9_clicked() {
 QFile file("data.txt");
 if (!file.open(QFile::WriteOnly | QIODevice::Text)) {
  QMessageBox::critical(this,tr("Ошибка"),tr("Не могу записать в data.txt"));
  return;
 }
 for (int row = 0; row < ui->listWidget->count(); row++) {
  QListWidgetItem *item = ui->listWidget->item(row);
  QTextStream out(&file);
  out << item->text() << "\n";
 }
 file.close();
}


void Widget::on_pushButton_10_clicked() {
 QFile file("data.txt");
 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
  QMessageBox::critical(this,tr("Ошибка"),tr("Не могу прочитать data.txt"));
  return;
 }
 QTextStream in(&file);
 while (!in.atEnd()) {
  QString line = in.readLine();
  QListWidgetItem *item = new QListWidgetItem;
  item->setText(line);
  ui->listWidget->addItem(item);
 }
 file.close();
}

Слот 10 не очищает список перед загрузкой в него элементов из файла, при необходимости это легко исправить с помощью слота 6.

Нам остаётся дать кнопкам чуть более понятные надписи, дважды кликнув по каждой и вписав новый текст, а потом заархивировать проект.

 Скачать этот проект QT 5.X в архиве .zip, папка уже создана внутри архива (5 Кб)

P.S. Замена QRegExp на QRegularExpression в Qt6.

06.04.2021, 13:10 [13342 просмотра]


теги: учебное список программирование c++ qt

К этой статье пока нет комментариев, Ваш будет первым