БлогNot. QT: список с автосохранением

QT: список с автосохранением

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

Запустив QT Creator, создадим проект на основе шаблона QWidget и сделаем форму во встроенном дизайнере, действуя, как в этой заметке, только на шаге 7 перетащим на форму компонент QListWidget, справа от него поместим QLabel и нажмём на шаге 8 комбинацию клавиш Ctrl+H (скомпоновать по горизонтали) вместо Ctrl+L.

В правую ячейку получившейся сетки под текстовой меткой последовательно поместим однострочное поле ввода QLineEdit, пять кнопок QPushButton и Vertical Spacer, который "прижмёт" компоненты к верхнему краю ячейки, получится вот такая форма:

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

Имеющийся в widget.cpp конструктор виджета изменим так, чтобы включить в него действия, нужные для загрузки элементов списка из текстового файла data.txt, находящегося в текущей папке приложения. Если файл не существует, создадим 2 элемента "вручную", просто для демонстрации того, как это делается.

Элемент 1 добавляется как строка, а элемент 2 - как объект класса QListWidgetItem.

Вот что вышло:

Widget::Widget(QWidget *parent) :  QWidget(parent), ui(new Ui::Widget) {
 ui->setupUi(this);
 ui->lineEdit->setFocus();
 //Автозагрузка списка при запуске виджета
 QFile file("data.txt");
 if (file.open(QIODevice::ReadOnly)) {
  QTextStream stream(&file);
  while (!stream.atEnd()) {
   QString s = stream.readLine();
   ui->listWidget->addItem(s.trimmed());
  }
  file.close();
 }
 else {
  //Как добавить элемент-строку:
  ui->listWidget->addItem(QString(tr("Элемент 1")));
  //Как добавить элемент QListWidgetItem:
  QListWidgetItem *item = new QListWidgetItem();
  item->setText(QString(tr("Элемент 2")));
  item->setToolTip(QString(tr("Подсказка")));
  ui->listWidget->addItem(item);
 }
}

По щелчку на элементе списка будем получать его текущий элемент в виде строки QString и дублировать его в поле ввода QLineEdit.

Чтобы получить пустую подпрограмму-"заготовку" достаточно в режиме дизайна формы щёлкнуть правой кнопкой мыши на списке и перейти к слоту clicked.

Для кнопок будем выбирать аналогичный слот (см. шаги 10-11 здесь).

void Widget::on_listWidget_clicked(const QModelIndex &index){
 this->unselectAll();
 ui->listWidget->setSelectionMode(QAbstractItemView::SingleSelection);
 ui->lineEdit->setText( //Получить текущий элемент как строку
  ui->listWidget->currentItem()->text());
}

Служебный метод unselectAll будет снимать выделение со всего, что было выделено в списке, вот его код, конечно, нужно не забыть указать прототип метода в файле widget.h, как и прописать там нужные заголовочные файлы (см. код этого файла в конце заметки).

void Widget::unselectAll () {
 //Снять выделение со всего, что было выделено:
 QList <QListWidgetItem *> items = ui->listWidget->selectedItems();
 foreach (QListWidgetItem *item,items)
  item->setSelected(false);
}

Все коды обработки нажатий кнопок приведены ниже, они показывают, как работать с Item-based списком, не заботясь о модели, и выполнять основные действия по добавлению уникального элемента, удалению выбранного элемента, поиску в списке по неполному соответствию строки (найденные элементы выделяются), сортировке списка в прямом и обратном порядке, а также полной очистке списка с подтверждением действия.

void Widget::on_pushButton_clicked() { //Добавить
 QString text = ui->lineEdit->text().trimmed().simplified();
  //Почистить строку от лишних разделителей
 if (text.isEmpty()) return;
 QList <QListWidgetItem *> list0 = //Поискать элемент
  ui->listWidget->findItems(text,Qt::MatchContains|Qt::MatchFixedString);
 if (list0.count()) { //Уже есть такой - заменить элемент
  QListWidgetItem *item=list0.at(0);
  int index = ui->listWidget->row(item);
  ui->listWidget->setCurrentRow(index);
  on_pushButton_2_clicked(); //Удалить старый
  ui->listWidget->insertItem(index,text); //Вставить новый
 }
 else { //Или добавить новый
  ui->listWidget->addItem(text);
 }
 ui->lineEdit->setText("");
 ui->lineEdit->setFocus();
}

void Widget::on_pushButton_2_clicked() { //Удалить
 int row = ui->listWidget->currentRow();
 if (row<0) {
  QMessageBox msgBox;
  msgBox.setText(QString(tr("Удаление элемента")));
  msgBox.setInformativeText(QString(tr("Выберите элемент для удаления")));
  msgBox.setStandardButtons(QMessageBox::Ok);
  msgBox.exec();
  return;
 }
 QListWidgetItem *item =
  ui->listWidget->takeItem(row);
 ui->lineEdit->setText(item->text()); //Скопировать текст удалённого
 ui->listWidget->setCurrentRow(row);
}

void Widget::on_pushButton_3_clicked() { //Поиск
 //Разрешить выделять несколько элементов:
 ui->listWidget->setSelectionMode(QAbstractItemView::MultiSelection);
 this->unselectAll();
 //Поискать text в списке:
 QString text = ui->lineEdit->text().trimmed().simplified();
 if (text.isEmpty()) return;
 QList <QListWidgetItem *> selected =
  ui->listWidget->findItems (text,
   Qt::MatchContains|Qt::MatchFixedString);
 if (selected.count()) { //Найдено
  foreach (QListWidgetItem *item,selected)
   item->setSelected(true); //Выделить найденное
 }
 else { //Не найдено
  QMessageBox msgBox;
  msgBox.setText(QString(tr("Ничего не найдено")));
  msgBox.exec();
 }
}

void Widget::on_pushButton_4_clicked() { //Сортировка
 //ui->listWidget->setSortingEnabled(true);
 static int Order = Qt::DescendingOrder;
 if (Order==Qt::AscendingOrder) {
  Order = Qt::DescendingOrder;
  ui->listWidget->sortItems(Qt::DescendingOrder);
  ui->pushButton_4->setText(QString(tr("Sort(v)")));
 }
 else {
  Order = Qt::AscendingOrder;
  ui->listWidget->sortItems(Qt::AscendingOrder);
  ui->pushButton_4->setText(QString(tr("Sort(^)")));
 }
}

void Widget::on_pushButton_5_clicked() { //Очистить
 QMessageBox msgBox;
 msgBox.setText(QString(tr("Очистка списка")));
 msgBox.setInformativeText(QString(tr("Вы уверены?")));
 msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
 msgBox.setDefaultButton(QMessageBox::Yes);
 if (msgBox.exec()==QMessageBox::Yes)
  ui->listWidget->clear();
}

Осталось предусмотреть в виджете обработчик события "закрытие окна" и вписать туда код для сохранения элементов списка в файле.

void Widget::closeEvent(QCloseEvent *event) {
 //Автосохранение списка при выходе - событие closeEvent
 int size=ui->listWidget->count();
 if (size) {
  QFile file("data.txt");
  if (file.open(QIODevice::WriteOnly)) {
   QTextStream stream(&file);
   for (int i=0; i<size; i++) {
    stream << ui->listWidget->item(i)->text().trimmed().simplified()
           << (i<size-1 ? "\r\n" : "");
   }
   stream.flush(); file.close();
  }
 }
}

Этот метод также прописывается в файле widget.h, со всеми предыдущими изменениями он получается таким:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget> /* необходимые библиотеки */
#include <QtWidgets>
#include <QEvent>
#include <QTextStream>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private slots:
    void on_listWidget_clicked(const QModelIndex &index);
    void on_pushButton_clicked();
    void on_pushButton_2_clicked();
    void on_pushButton_3_clicked();
    void on_pushButton_4_clicked();
    void on_pushButton_5_clicked();
    void closeEvent(QCloseEvent *); /* обработчик закрытия окна */
private:
    Ui::Widget *ui;
    void unselectAll (); /* метод для снятия выделения с элементов */
};

#endif // WIDGET_H

Приложение можно собирать и запускать, проверено в QT 5.X.

 Скачать папку с этим проектом QT5 в архиве .zip (4 Кб)

12.04.2018, 09:36 [3662 просмотра]


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

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