БлогNot. QT: работаем со списком строк QStringList и стандартными контейнерами Tulip

QT: работаем со списком строк QStringList и стандартными контейнерами Tulip

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

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

Показанный в заметке проект WidgetTemplate - своего рода шаблон приложения для обработки списка строк. Строки выгружаются из многострочного текстового поля QTextEdit в QStringList, а затем обрабатываются удобными методами QStringList и QString, после чего изменённый список загружается обратно. В моём случае обработка сводится к фильтрации строк списка (удаление пустых и состоящих только из разделителей строк, затем сжатие лишних разделителей между словами строк и удаление лишних пробелов в начале или конце строки.

Также пример показывает, как сделать верхнее меню в потомке QWidget, просто прописать всё "ручками", готового в сети не нашёл. В норме QWidget не имеет встроенного меню и не работает, например, "прямой" вызов menuBar() из многочисленных книжных и сетевых примеров. В принципе, проблема решается просто - достаточно сделать виджет наследником QApllication, а не QWidget. Но можно и сделать меню не визуально, а логически, оставаясь наследником QWidget.

Заслуживает внимания и "современный" способ центрирования окна виджета по декстопу с помощью метода setGeometry. Для этого нам пришлось передать в конструктор виджета ссылку на экземпляр родительского приложения QApplication.

Проект виджета создан на основе класса QWidget без создания формы (так как весь интерфейс спроектирован логически в конструкторе).

Файл widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QtWidgets>
class Widget : public QWidget {
    Q_OBJECT
private:
    QVBoxLayout *layout;
    QTextEdit *textEdit;
    QMenuBar *menuBar;
    QMenu *fileMenu;
    QAction *newAction,*doAction,*exitAction;
private slots:
    void newFile(void);
    void doFile(void);
    void exitFile(void);
public:
    Widget(QApplication *a=0,QWidget *parent = 0);
    ~Widget();
};
#endif // WIDGET_H
Файл widget.cpp
#include "widget.h"

Widget::Widget(QApplication *a,QWidget *parent) : QWidget(parent) {
 this->menuBar = new QMenuBar(this);
 this->fileMenu = new QMenu(tr("&Файл"));
 this->menuBar->addMenu(this->fileMenu);
 this->newAction = new QAction(tr("&Новый"),this->fileMenu);
 this->doAction = new QAction(tr("&Выполнить"),this->fileMenu);
 this->exitAction = new QAction(tr("В&ыход"),this->fileMenu);
 this->fileMenu->addActions(
  QList <QAction *> () << this->newAction << this->doAction << this->exitAction
 );
 QObject::connect(this->newAction,SIGNAL(triggered()),
  this,SLOT(newFile()));
 QObject::connect(this->doAction,SIGNAL(triggered()),
  this,SLOT(doFile()));
 QObject::connect(this->exitAction,SIGNAL(triggered()),
  this,SLOT(exitFile()));
 this->textEdit = new QTextEdit(this);
 this->layout = new QVBoxLayout(this);
 this->layout->setMargin(5);
 this->layout->addWidget(this->textEdit);
 this->layout->setMenuBar(this->menuBar);
 this->setLayout(this->layout);
 this->setWindowTitle("Мой виджет");
 this->setMinimumSize(320,240);
 this->resize(640,480);
 this->setGeometry(QStyle::alignedRect(
   Qt::LeftToRight,Qt::AlignCenter,this->size(),
   a->desktop()->availableGeometry()));
}

Widget::~Widget() { }

void Widget::newFile(void) { this->textEdit->clear(); }

void Widget::doFile(void) {
 QString String = this->textEdit->toPlainText();
 QStringList list = String.split("\n");
 //обработка QStringList
 QRegExp regExp("^(?!\\s*$).+");
 list = list.filter(regExp); //убрали строки только из разделителей
 list.replaceInStrings(QRegularExpression("\\s+")," ");
  //убрали лишние разделители между словами
 list.replaceInStrings(QRegularExpression("^\\s+|\\s+$"),"");
  //убрали лишние разделители в начале или конце строк
 this->textEdit->clear();
 this->textEdit->append(list.join("\n"));
}

void Widget::exitFile(void) { QApplication::quit(); }
Файл main.cpp
#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[]) {
 QApplication a(argc, argv);
 Widget w(&a);
 w.show();
 return a.exec();
}

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

Для демонстрации того, что работа с другими контейнерами может быть организована аналогично, изменим только наполнение метода doFile(). В показанном ниже коде вводимые пользователем строки вида "ключ:значение", где "ключ" – целое число, а "значение" – строка, записываются в мультихэш (ассоциативный массив, в котором одному ключу может соответствовать несколько значений).

Полученный мультихэш выводится в консоль отладки. В реальной задаче после его формирования можно выполнять любые требуемые действия с данными, например, получить список всех значений, соответствующих ключу "0", мы можем так:

QList <QString> lst=hash.values(0);

Пример пользовательского ввода для формирования хэша:

1:345

1
2
3:44
1:567
1:789
string

И вывод в отладочной консоли QT для него:

Element:  "1" 
Element:  "1:345" 
Element:  "1:567" 
Element:  "1:789" 
Element:  "2" 
Element:  "3:44" 
Element:  "string" 
Key= 0 Value= "1" 
Key= 1 Value= "2" 
Key= 1 Value= "789" 
Key= 1 Value= "567" 
Key= 1 Value= "345" 
Key= 2 Value= "string" 
Key= 3 Value= "44" 

Вот изменённый метод doFile(), также длинным комментарием пояснено первое регулярное выражение :)

void Widget::doFile(void) {
 QString String = this->textEdit->toPlainText();
 QStringList list = String.split("\n");
 list.removeDuplicates(); //Убрали дублирующие друг друга строки
 list.sort(Qt::CaseInsensitive);//Отсортировали с игнорированием регистра букв
 QRegExp regExp("^(?!\\s*$).+");
 //^   (?!        \\s         *         $)   .+
 //нач негативная разделитель ноль      кон  любой символ
 //    проверка               или более      хотя бы один
 //Получается:
 //"если в строке есть хоть один символ - не надо разделителей слева от него"
 list = list.filter(regExp); //убрали строки только из разделителей
 list.replaceInStrings(QRegularExpression("\\s+")," ");
  //убрали лишние разделители между словами
 list.replaceInStrings(QRegularExpression("^\\s+|\\s+$"),"");
  //убрали лишние разделители в начале или конце строк
 //Пользовательская часть виджета
 QMultiHash <int,QString> hash; //Ключ - число, значения - строки
  QStringList::iterator it = list.begin();
  int key=0; //Ключ для элементов, которым его не дал пользователь
  for (;it!=list.end();++it) { //Пройти по списку элементов "ключ:значение"
   QStringList item = (*it).split(":",QString::SkipEmptyParts);
    //разбить элемент по разделителю ":"
   if (item.size()<2) hash.insertMulti(key++,item.at(0));
   else hash.insertMulti(item.at(0).toInt(),item.at(1));
    //Добавили в хэш ключ (наш или заданный пользователем) и значение
   qDebug() << "Element: " << (*it);
  }
  //Вывести в консоль отладки мультихэш
  QMultiHash<int,QString>::iterator i = hash.begin();
  for (;i!=hash.end();++i)
      qDebug() << "Key=" << i.key() << "Value=" << i.value();
 //конец пользовательской части
 this->textEdit->clear();
 this->textEdit->append(list.join("\n"));
}

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

24.03.2017, 15:27 [19823 просмотра]


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

показать комментарии (1)