[Решено] Qt: функционал tail -f в QPlainTextEdit (Как отображать в окне текстовый файл в реальном времени?)

Модератор: Модераторы разделов

Ответить
FlySnake
Сообщения: 992
ОС: openSUSE
Контактная информация:

[Решено] Qt: функционал tail -f в QPlainTextEdit

Сообщение FlySnake »

Всем привет!
В заголовке темы описана суть вопроса. Надо в Qt-шном окошке отображать файл, дописываемый другой программой (лог).
Просто чтение файла в QPlainTextEdit сделано так:

Код: Выделить всё

QFile inputFile(files->fileInfo(index).absoluteFilePath());
inputFile.open(QIODevice::ReadOnly);
QTextStream in(&inputFile);
QString line = in.readAll();
inputFile.close();
ui->plainTextEdit->setPlainText(line);
QTextCursor cursor = ui->plainTextEdit->textCursor();
cursor.movePosition(QTextCursor::End);
ui->plainTextEdit->setTextCursor(cursor);

Но как сделать чтобы он обновлялся раз в 1-2-3 секунды? Пните в правильную сторону.

И ещё попутно вопрос. Как задать для QPlainTextEdit символ новой строки? В файле новая строка \r\n и это отображается как 2 новых строки, а надо чтобы одна, в идеале определять на какой ОС запущена программа и брать родной символ новой строки.
Спасибо сказали:
Аватара пользователя
Crazy
Сообщения: 862
Статус: Адепт Дзен.
ОС: Mint, Win7.

Re: [Решено] Qt: функционал tail -f в QPlainTextEdit

Сообщение Crazy »

Проще всего сигнал-слот с QTimer, или наследоваться от QObject и юзать событие timerEvent.
по поводу \r\n

Код: Выделить всё

QTextStream ( FILE * fileHandle, QIODevice::OpenMode openMode = QIODevice::ReadWrite )

А именно флаг QIODevice::Text .

Desipere in loco
Спасибо сказали:
FlySnake
Сообщения: 992
ОС: openSUSE
Контактная информация:

Re: [Решено] Qt: функционал tail -f в QPlainTextEdit

Сообщение FlySnake »

QIODevice::Text то что надо, спасибо
А с таймером Вы имеете в виду перезагружать файл по таймеру? Так я пробовал, когда файл большой (более 1мБ) то это сильно тормозит.
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: [Решено] Qt: функционал tail -f в QPlainTextEdit

Сообщение NickLion »

Не закрывайте файл, а по таймеру делайте что-то вроде:

Код: Выделить всё

    if (!inputFile.atEnd()) {
        QTextStream in(&inputFile);
        ui->tail->appendPlainText(in.readAll());
    }

Спасибо сказали:
Аватара пользователя
Crazy
Сообщения: 862
Статус: Адепт Дзен.
ОС: Mint, Win7.

Re: [Решено] Qt: функционал tail -f в QPlainTextEdit

Сообщение Crazy »

Я бы сохранял длину файла, а по таймеру устанавливал бы через seek на начало новых строк. И считывал бы не весь файл, а только новые строки.
Все это дело можно пихать в QStringList или QStringListModel.

Desipere in loco
Спасибо сказали:
FlySnake
Сообщения: 992
ОС: openSUSE
Контактная информация:

Re: [Решено] Qt: функционал tail -f в QPlainTextEdit

Сообщение FlySnake »

NickLion писал(а):
18.05.2012 20:15
Не закрывайте файл, а по таймеру делайте что-то вроде:

А то что он периодически открывается другой программой это ок?

Crazy писал(а):
18.05.2012 20:58
Я бы сохранял длину файла, а по таймеру устанавливал бы через seek на начало новых строк. И считывал бы не весь файл, а только новые строки.

Спасибо за идею
Сделал так:

Код: Выделить всё

private:
...
QPair<QFile *, uint64> *logfile;
...

Слот вызываемый по таймеру:

Код: Выделить всё

void TailWindow::read_file()
{
    if(!logfile->first)
    {
        qDebug() << "file not open";
        return;
    }
    logfile->first->open(QIODevice::ReadOnly | QIODevice::Text);
    if(!logfile->first)
    {
        QMessageBox::critical(this, tr("Error opening file"), tr("Cannot open file") + logfile->first->fileName());
        return;
    }
    logfile->first->seek(logfile->second);
    if(logfile->first->atEnd())
    {// not changed
        logfile->first->close();
        return;
    }
    QString text("");
    while(!logfile->first->atEnd())
    {
        text += logfile->first->readLine();
    }
    ui->plainTextEdit->appendPlainText(text);
    logfile->second = logfile->first->pos();
    logfile->first->close();
}

При открытии файла 24 мБ программа съедает 150мБ ОЗУ, но затоЦП около 0. Наверное надо сделать чтобы первый раз при открытии читала не с 0 позиции, а несколько крайних строк.

Только 1 "фича", не знаю полезно ли оставить или бороться. Каждое новое чтение по таймеру добавляет новую строку, т.е. получается не сплошняком текст, а разделённый пустыми строками на каждом новом чтении файла.
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: [Решено] Qt: функционал tail -f в QPlainTextEdit

Сообщение NickLion »

FlySnake писал(а):
20.05.2012 18:50
NickLion писал(а):
18.05.2012 20:15
Не закрывайте файл, а по таймеру делайте что-то вроде:

А то что он периодически открывается другой программой это ок?

Да, вполне.

FlySnake писал(а):
20.05.2012 18:50
При открытии файла 24 мБ программа съедает 150мБ ОЗУ, но затоЦП около 0. Наверное надо сделать чтобы первый раз при открытии читала не с 0 позиции, а несколько крайних строк.

setMaximumBlockCount()
заодно убирает механизм undo/redo, который у Вас съедает память тоже прилично, небось (setUndoRedoEnabled(false) хотя бы сделайте, если ограничние не нужно).

FlySnake писал(а):
20.05.2012 18:50
Только 1 "фича", не знаю полезно ли оставить или бороться. Каждое новое чтение по таймеру добавляет новую строку, т.е. получается не сплошняком текст, а разделённый пустыми строками на каждом новом чтении файла.

Особенность append...() можете попробовать insertPlainText, но автоматически не будет прокручиваться. Или убирать перевод строки в данных, которые добавляете.
Спасибо сказали:
Аватара пользователя
Crazy
Сообщения: 862
Статус: Адепт Дзен.
ОС: Mint, Win7.

Re: [Решено] Qt: функционал tail -f в QPlainTextEdit

Сообщение Crazy »

NickLion писал(а):
20.05.2012 19:13
Или убирать перевод строки в данных, которые добавляете.

Смотри void QString::chop ( int n )

Desipere in loco
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: [Решено] Qt: функционал tail -f в QPlainTextEdit

Сообщение NickLion »

Правда, подвох всё равно остаётся. Никто не гарантирует, что в момент считывания будут записаны все данные (не атомарная операция), и считана может быть половина строки, переноса строки ещё не будет.
Спасибо сказали:
FlySnake
Сообщения: 992
ОС: openSUSE
Контактная информация:

Re: [Решено] Qt: функционал tail -f в QPlainTextEdit

Сообщение FlySnake »

NickLion писал(а):
20.05.2012 19:13
setMaximumBlockCount()

Полезная штука, спасибо, но память жрёт не это, а выгрузка всего файла в QString. Сделал так:
при открытии файла

Код: Выделить всё

logfile->first->seek(logfile->first->size() - 65536);
logfile->first->readLine();
quint64 off = logfile->first->pos();
logfile->first->close();
logfile->second = logfile->first->size() > 1048576  ? off : 0;

Т.е. если файл больше 1Мб, то прокрутить его назад на 64Кб, считать строку (гарантирует что следующая строка будет с самого начала) и сохранить начальную позицию. Если меньше 1Мб, то начало с 0 позиции.
Так тормоза исчезли и памяти вся программа занимает 16Мб

NickLion писал(а):
20.05.2012 23:28
Правда, подвох всё равно остаётся. Никто не гарантирует, что в момент считывания будут записаны все данные (не атомарная операция), и считана может быть половина строки, переноса строки ещё не будет.

Можно проверять что на конце строки перед обрезанием (сорри за каламбур :) )

Код: Выделить всё

if(text.right(1) == "\r" || text.right(1) == "\n")
{
        text.chop(1);
        if(text.right(1) == "\r" || text.right(1) == "\n")
        {
            text.chop(1);
        }
}

Вроде работает.

Ещё попутный вопрос. Когда я захочу раскрасить цветами вывод файла (ошибки красным, незначительные сообщения серым и т.д.) то придётся использовать QTextEdit или QTextBrowser и форматировать строки в HTML ? Но у QTextEdit и унаследованного QTextBrowser нет setMaximumBlockCount(). Как же в этих виджетах задать максимальное кол-во символов для отображения?
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: [Решено] Qt: функционал tail -f в QPlainTextEdit

Сообщение NickLion »

QPlainTextEdit поддеживает форматирование текста:
Text can be formatted in a limited way, either using a syntax highlighter (see below), or by appending html-formatted text with appendHtml(). While QPlainTextEdit does not support complex rich text rendering with tables and floats, it does support limited paragraph-based formatting that you may need in a log viewer.

Т.е. 3 возможных пути для раскраски:
подсветка синтаксиса
добавлять HTML (не советую, придётся проверять, что строки не содержат тегов — заменять < на &lt; например)
setCurrentCharFormat()
Спасибо сказали:
Аватара пользователя
Crazy
Сообщения: 862
Статус: Адепт Дзен.
ОС: Mint, Win7.

Re: [Решено] Qt: функционал tail -f в QPlainTextEdit

Сообщение Crazy »

NickLion писал(а):
21.05.2012 06:47
добавлять HTML (не советую, придётся проверять, что строки не содержат тегов — заменять < на &lt; например)

С этим справиться QString Qt::escape ( const QString & plain ).
Для подсветки HTML скорее всего придется использовать регулярные выражения. Если пойдешь этим путем, то стоит посмотреть на colorgcc

Desipere in loco
Спасибо сказали:
FlySnake
Сообщения: 992
ОС: openSUSE
Контактная информация:

Re: [Решено] Qt: функционал tail -f в QPlainTextEdit

Сообщение FlySnake »

Спасибо всем за помощь ;)
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: [Решено] Qt: функционал tail -f в QPlainTextEdit

Сообщение NickLion »

Crazy писал(а):
21.05.2012 10:47
NickLion писал(а):
21.05.2012 06:47
добавлять HTML (не советую, придётся проверять, что строки не содержат тегов — заменять < на &lt; например)

С этим справиться QString Qt::escape ( const QString & plain ).

Да, я эту функцию и имел в виду, просто не вспомнил как называется сходу, а искать было лень.

Но всё равно лучше этого не делать, а либо использовать QSyntaxHighlighter или QTextCharFormat. Не проверял — имхо.
Спасибо сказали:
Ответить