Open source
Qt
16 March 2014

SpeedReader — Qt библиотека для скорочтения

image

Предисловие


Некоторое время назад на Хабре была новость о Spritz — программной реализации техники скорочтения, основанной на быстрой смене слов в виджете с определенным центрированием самого слова внутри виджета, а чуть позже и другая новость. Так как тема довольно актуальная я, недолго думая, решил реализовать нечто подобное и универсальное, с возможностью встраивания такого виджета для скорочтения в программы на различных платформах (win, linux, mac, android). Исходя из этого условия был выбран Qt фрейморк с его широкой поддержкой различных платформ.

То, что получилось и как с этим работать описано ниже. Кому интересно, добро пожаловать.

Как это использовать?


1. Подключить заголовочные файлы:
#include "SpeedReader/speedreader.h"
#include "SpeedReader/txtreader.h" // или другой наследник TextFormatReader


2. Создать объекты:
TxtReader *pTxtReader = new TxtReader("path_to_file", "UTF-8"); // параметры: путь к файлу и название текстового кодека, в котором сохранен файл
SpeedReader *pSpeedReader = new SpeedReader(pTxtReader);


3. Установить настройки ридера:
pSpeedReader->setReadingSpeed(300); // скорость в словах в минуту
pSpeedReader->setCommaPauseTime(150); // пауза при достижении запятой (в мс)
pSpeedReader->setDotPauseTime(200); // пауза при достижении конца предложения (в мс)
pSpeedReader->setCurrentPosition(0); // позиция начала чтения


4. Положить SpeedReaderLabel виджет на форму (можно положить QLabel, а затем преобразовать в SpeedReaderLabel). Установить цвет подсвечиваемого символа:
ui->speedReaderLabel->setSymbolColor("red"); 


5. Подсоединить сигналы объекта класса SpeedReader к слотам виджета SpeedReaderLabel:
connect(pSpeedReader, SIGNAL(nextWordAvailable(QString, int)), ui->speedReaderLabel, SLOT(processNextWordAvailable(QString, int)));
connect(pSpeedReader, SIGNAL(wordOffset(int)), ui->speedReaderLabel, SLOT(processWordOffset(int)));


6. Старт/стоп чтения:
pSpeedReader->startReading();
pSpeedReader->stopReading();


7. Так же есть возможность перехватить некоторые необязательные сигналы объекта класса SpeedReader, такие как:
SIGNAL(error(SpeedReaderError)) // ошибка
SIGNAL(endOfBook()) // высылается при достижении конца файла
SIGNAL(readingProgress(double)) // прогресс чтения (от 0 до 1)


Немного о том, как это все работает. Реализация ядра библиотеки


Выделив в выходной день несколько часов, сел за продумывание идеи и ее программирование. Была выбрана такая схема: класс для переключения слов (SpeedReader) + базовый абстрактный класс для считывания текстовых файлов в разных форматах (TextFormatReader), от которого необходимо наследовать конкретные «читалки форматов» (например, для примера реализован простейший класс-читалка *.txt формата — TxtReader).

Центральный класс SpeedReader

Основной задачей объектов этого класса является переключение слова с определенной скоростью и вообще, вести себя в соответствии с заданными настройками. Посмотрим на публичный интерфейс класса:
class SpeedReader : public QObject
{
   ...

    void setCurrentPosition(const int &currentPosition); // установка начальной позиции для чтения
    void setCommaPauseTime(const int &comaPauseTime); // установка паузы при достижении запятой (в мс)
    void setDotPauseTime(const int &dotPauseTime); // установка паузы при достижении конца предложения (в мс)
    void setReadingSpeed(int wordPerMinute); // установка скорости (слов в минуту)
    void setEnableShifting(const bool &enableShifting); // установка сдвига слова (по умолчанию включено)

// соответствующие get методы
    QStringList getWordsToRead() const; // возвращает загруженный из файла список слов
    int getCurrentPosition() const;
    int getComaPauseTime() const;
    int getDotPauseTime() const;
    int getReadingSpeed() const;
    int getWordsCount() const; // возвращает количество слов, загруженных из файла
    bool isEnableShifting() const;

    void startReading(); // инициализация переключения слов
    void stopReading(); // остановка переключения слов

    ...
}


Само переключение слов происходит за счет высылки сигнала объектом класса SpeedReader, содержащим слово для отображения в виджете. Но о виджете позже.

Доступные сигналы SpeedReader для обработки:
signals:
    // сигналы для виджета (в принципе не нужно знать в рамках этой библиотеки для чего они нужны. просто опишу, что они есть)
    void nextWordAvailable(QString, int); // сигнал, высылающий слово и сдвиг - количество символов
    void wordOffset(int); // сигнал, высылающий максимальную длину из всех слов

    // сигналы необязательные для перехвата
    void readingProgress(double); // сигнал, высылающий прогресс прочтенных слов (от 0 до 1)
    void error(SpeedReaderError); // сигнал, высылающий ошибку в случае ее возникновения
    void endOfBook(); // сигнал, сигнализирующий о завершении чтения файла


TextFormatReader — базовый абстрактный класс для реализации чтения различных текстовых форматов

Все, что нужно знать об этом классе, это то, что в нем есть метод
virtual QStringList getWords() = 0;
, который нужно переопределить в классах наследниках. В этом методе как раз и происходит парсинг содержимого текстового формата. Например, в классе TxtReader в этом методе происходит простейший парсинг, а именно замена двух пробелов на один и удаление символов переноса строк.

class TextFormatReader: public QObject
{
    ...
    public:
         TextFormatReader(const QString &fileName, const QString &textCodecName);
         virtual QStringList getWords() = 0; // тот самый метод для переопределения
         void openBook(const QString &filePath);
    ...
};


Собственно, это и есть ядро библиотеки.

Реализация виджета SpeedReaderLabel


Виджет для отображения слов — наследник QLable, на котором рисуются линии разметки (как на рисунке в начале статьи). Основная задача виджета — отображать слова, присланные объектом класса SpeedReader. Давайте посмотрим на публичный интерфейс виджета:

class SpeedReaderLabel : public QLabel
{
    ...

    public slots:
        void processWordOffset(const int &verticalPointerOffset); // помните сигнал nextWordAvailable класса SpeedReader? Этот слот обрабатывает тот сигнал. В этом слоте устанавливается смещение вертикального указателя в виджете (см. картинку вначале поста)
        void processNextWordAvailable(QString w, int shift); // та же история. Слот устанавливает слово в виджет и сдвигает его на shift * ширина символа текущего шрифта

    ...
};


Поддержка других текстовых форматов (fb2, html и других)


Для того, чтобы добавить поддержку других текстовых форматов, необходимо создать класс и унаследовать его от TextFormatReader:

1. Необходимо унаследоваться от TextFormatReader, вызвать конструктор предка и переопределить один метод:
#ifndef SOMEREADER_H
#define SOMEREADER_H
 
#include "textformatreader.h"
 
class SomeReader : public TextFormatReader 
{
     public:
         SomeReader(const QString &fileName, const QString &textCodecName) : TextFormatReader(fileName, textCodecName) {} // вызов конструктора родительского класса
         QStringList getWords(); // этот метод необходимо переопределить
};
 
#endif // SOMEREADER_H


2. Переопределение getWords() метода:
QStringList SomeReader::getWords()
{
    QString textToParse = this->text; // this->text - "сырой" текст из открытого файла
 
    // здесь парсим текст (удаляем все теги и прочее прочее)
   textToParse = textToParse.replace("someTag", "");

   return countWords(textToParse); // необходимо возвратить результат countWords(QString text) метода, передав в него распарсенный текст
}


3. Теперь можно открывать файлы определенного в нашем классе формата:
SomeReader *pSomeReader = new SomeReader("path_to_file", "UTF-8");
SpeedReader *pSpeedReader = new SpeedReader(pSomeReader);
...


Ну а далее все, как в начале статьи в разделе «Как это использовать?».

Заключение


В итоге была разработана небольшая библиотека, которая позволит встраивать данную технологию скорочтения в программы на различных платформах. Позволю себе поместить здесь ссылку на проект в Google Code: SpeedReader on google code. В репозитории две папки: SpeedReader (в ней файлы библиотеки) и SpeedReaderTest (минимальный работающий проект с использованием библиотеки. *.txt файл необходимо поместить в папку с исполняемым скомпилированным файлом программы либо прописать свой путь к файлу). С удовольствием отвечу на все вопросы в комментариях, если возникнут.

+50
29.8k 174
Comments 43
Top of the day