/
MaximU
/
application
Обзор
Документация
Войти
/
MaximU
/
application
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
replaywidget.cpp
1 188 строк
32 KB
user
ui создается динамически.
22 июл 2026, 14:41
22 июл 2026, 14:41
2251cf8
Код
Авторство
О чём код?
#include "replaywidget.h" #include <QCloseEvent> #include <QFileDialog> #include <QMessageBox> #include "ui_replaywidget.h" #include "utc.h" std::shared_ptr<char[]> acquireInputBuffer(); void releaseInputData(std::shared_ptr<char[]> data); void releaseInputBuffer(std::shared_ptr<char[]> buffer); /*! * \brief Конструктор. * \param parent Родительский виджет. * \param signature Сигнатура файлов записей. * \param version Версия файлов записей. * \param description Описание файлов записей. * \param extension Расширение файлов записей. * \param types Список типов массивов * \param mode Режим работы (чтение/запись). * \param Признак начала записи при старте программы. */ ReplayWidget::ReplayWidget(QWidget *parent, const QString &signature, int version, const QString &description, const QString &extension, const QList<int> &types, QIODevice::OpenMode mode, bool writeAtStartup) : QWidget(parent, Qt::Tool | Qt::WindowStaysOnTopHint), ui(new Ui::ReplayWidgetClass), mSignature(signature), mVersion(version), mDescription(description), mExtension(extension), mTypes(types) { ui->setupUi(this); ui->lineEditPortions->setVisible(false); ui->labelPlayPortions->installEventFilter(this); ui->lineEditPortions->installEventFilter(this); switch (mode) { case QIODevice::ReadWrite: setWindowTitle(tr("Запись/воспроизведение. ") + mDescription); setWindowIcon(QPixmap(":/ReplayWidget/resources/disk_blue.png")); break; case QIODevice::WriteOnly: setWindowTitle(tr("Запись. ") + mDescription); setWindowIcon(QPixmap(":/ReplayWidget/resources/disk_yellow.png")); break; case QIODevice::ReadOnly: setWindowTitle(tr("Воспроизведение. ") + mDescription); setWindowIcon(QPixmap(":/ReplayWidget/resources/disk_green.png")); break; } if ((mode & QIODevice::WriteOnly) == 0) { ui->tabWidget->removeTab(0); } if ((mode & QIODevice::ReadOnly) == 0) { ui->tabWidget->removeTab(1); } //// Запись. mRecordsDir = qApp->applicationDirPath() + "/Records"; mRecordAtStartup = ((mode & QIODevice::WriteOnly) == 0) ? false : writeAtStartup; //// Чтение. connect(&mTimer, &QTimer::timeout, this, &ReplayWidget::slot_timer_timeout); mTimerElapsed.setInterval(TimerElapsedInterval); connect(&mTimerElapsed, &QTimer::timeout, this, &ReplayWidget::slot_timerElapsed_timeout); //// updateUi(); } /*! * \brief Деструктор. */ ReplayWidget::~ReplayWidget() { delete ui; } /*! * \brief Фильтр событий * \param object * \param event */ bool ReplayWidget::eventFilter(QObject *object, QEvent *event) { if (object == ui->labelPlayPortions && event->type() == QEvent::MouseButtonDblClick) { if (!mPlayContext.fileName().isNull() && !mPlaying) { ui->lineEditPortions->move(ui->labelPlayPortions->pos()); ui->lineEditPortions->resize(ui->labelPlayPortions->size()); ui->lineEditPortions->setText(""); ui->lineEditPortions->setVisible(true); ui->lineEditPortions->setFocus(Qt::MouseFocusReason); } } if (object == ui->lineEditPortions && event->type() == QEvent::KeyPress) { if (((QKeyEvent *)event)->key() == Qt::Key_Escape) { ui->lineEditPortions->setVisible(false); } } return QWidget::eventFilter(object, event); } /*! * \brief Вызывается при отображении окна. */ void ReplayWidget::showEvent(QShowEvent *event) { Q_UNUSED(event) emit visibilityChanged(true); } /*! * \brief Вызывается при скрытии окна. */ void ReplayWidget::hideEvent(QHideEvent *event) { Q_UNUSED(event) emit visibilityChanged(false); } /*! * \brief Вызывается при попытке закрытия диалога. Запрашивает разрешение на закрытие. */ void ReplayWidget::closeEvent(QCloseEvent *event) { Q_UNUSED(event) mPlayRate = ui->sliderPlayRate->value(); if (mRecording || mPlaying) { QMessageBox messageBox(QMessageBox::Question, tr("Запись/Воспроизведение"), tr("Закрыть и продолжить запись/воспроизведение?"), QMessageBox::Yes | QMessageBox::No); if (messageBox.exec() == QMessageBox::No) { event->ignore(); } } } /*! * \brief Сохраняет настройки. * \param settings Объект сохранения настроек. */ void ReplayWidget::writeSettings(QSettings &settings) { show(); // Необходимо отобразить окно чтобы установлись координаты. Иначе для неоткрытых окон pos() будет равен (0,0). settings.beginGroup(mSignature); settings.setValue("pos", pos()); settings.setValue("currentTab", ui->tabWidget->currentIndex()); //// Запись. settings.beginGroup("Record"); settings.setValue("recordsDir", mRecordsDir); settings.setValue("limitType", mRecordLimitType); settings.setValue("limitBlocks", mRecordLimitPortions); settings.setValue("limitMBs", mRecordLimitMBs); settings.setValue("continue", mRecordContinue); settings.setValue("atStartup", mRecordAtStartup); settings.endGroup(); //// Воспроизведение. settings.beginGroup("Play"); settings.setValue("playRate", mPlayRate); settings.endGroup(); settings.endGroup(); } /*! * \brief Восстанавливает настройки. * \param settings Объект восстановления настроек. */ void ReplayWidget::readSettings(QSettings &settings) { settings.beginGroup(mSignature); if (settings.contains("pos")) { move(settings.value("pos", pos()).toPoint()); } ui->tabWidget->setCurrentIndex(settings.value("currentTab", 0).toInt()); //// Запись. settings.beginGroup("Record"); mRecordsDir = settings.value("recordsDir", mRecordsDir).toString(); mRecordLimitType = (RecLimitType)settings.value("limitType", MBs).toInt(); mRecordLimitPortions = settings.value("limitBlocks", mRecordLimitPortions).toInt(); mRecordLimitMBs = settings.value("limitMBs", mRecordLimitMBs).toInt(); mRecordContinue = settings.value("continue", mRecordContinue).toBool(); mRecordAtStartup = settings.value("atStartup", mRecordAtStartup).toBool(); settings.endGroup(); //// Воспроизведение. settings.beginGroup("Play"); mPlayRate = settings.value("playRate", mPlayRate).toInt(); settings.endGroup(); settings.endGroup(); //// Проверка признака записи при старте. if (mRecordAtStartup) { beginRecord(); } updateUi(); } /*! * \brief Обновляет содержимое окна. */ void ReplayWidget::updateView() { updatePlayStatus(); updateRecordStatus(); } /*! * \brief Устанавливает вид элементов управления в зависимости от состояния выполняемой операции. */ void ReplayWidget::updateUi() { QString text; //// Запись. ui->actionRecordStart->setEnabled(!mRecording); ui->actionRecordStop->setEnabled(mRecording); ui->comboMaxSizeUnits->setCurrentIndex(mRecordLimitType); switch (mRecordLimitType) { case MBs: text = QString::number(mRecordLimitMBs); break; case Portions: text = QString::number(mRecordLimitPortions); break; } if (!ui->editMaxSize->hasFocus()) { ui->editMaxSize->setText(text); } ui->checkboxRecordContinue->setChecked(mRecordContinue); ui->checkboxRecordAtStartup->setChecked(mRecordAtStartup); //// Воспроизведение. ui->actionPlayFile->setEnabled(!mPlaying); ui->actionPlayStart->setEnabled(mPlayContext.isOpen() && mPlayContext.portionCount() > 0 && !mPlaying); ui->actionPlayPause->setEnabled(mPlayContext.isOpen() && mPlayContext.portionCount() > 0); ui->actionPlayPause->setChecked(mPlayPause); ui->actionPlayStop->setEnabled(mPlayContext.isOpen() && mPlaying); ui->actionPlayForward->setEnabled(mPlayContext.isOpen() && mPlaying && mPlayPause); ui->sliderPlayProgress->setEnabled(!mPlaying); int value = (mPlayRate <= 1000) ? mPlayRate : (mPlayRate - 1000) / (10000. - 1000.) * (1900 - 1000) + 1000; ui->sliderPlayRate->setValue(value); if (mPlayRate <= 1000) { text = tr("%1 c.").arg(mPlayRate / 1000., 0, 'f', 3); } else { text = tr("%1 c.").arg(mPlayRate / 1000., 0, 'f', 1); } if (!ui->labelPlayRate->hasFocus()) { ui->labelPlayRate->setText(text); } } /*! * \brief Возвращает сигнатуру файла. * \return Сигнатура. */ QString ReplayWidget::signature() const { return mSignature; } /*! * \brief Возвращает версию файла. * \return Версия. */ int ReplayWidget::version() const { return mVersion; } /*! * \brief Возвращает описание файла. * \return Описание. */ QString ReplayWidget::description() const { return mDescription; } /*! * \brief Возвращает расширение файла. * \return Расширение файла. */ QString ReplayWidget::extension() const { return mExtension; } /*! * \brief Возвращает список типов массивов. * \return Список типов массивов. */ const QList<int> & ReplayWidget::types() const { return mTypes; } /*! * \brief Вызывает старт записи. */ void ReplayWidget::on_actionRecordStart_triggered() { beginRecord(); mRecordedBytesTotal = 0; updateRecordStatus(); updateUi(); } /*! * \brief Вызывает остановку записи. */ void ReplayWidget::on_actionRecordStop_triggered() { endRecord(); updateUi(); } /*! * \brief Вызывается при изменении списка типоа ограничения размера записи. * \param index Индекс типа ограничения в выпадающем списке. */ void ReplayWidget::on_comboMaxSizeUnits_currentIndexChanged(int index) { switch (index) { case 0: mRecordLimitType = MBs; break; case 1: mRecordLimitType = Portions; break; } updateUi(); } /*! * \brief Вызывается при нажатии Enter в окне редактирования ограничения размера записи. */ void ReplayWidget::on_editMaxSize_editingFinished() { bool ok; int value = ui->editMaxSize->text().toInt(&ok); value = std::max<int>(1, value); if (ok) { switch (mRecordLimitType) { case MBs: mRecordLimitMBs = value; break; case Portions: mRecordLimitPortions = value; break; } } updateUi(); } /*! * \brief Выполняется при изменении признака продолжения записи. * \param checked Значение признака. */ void ReplayWidget::on_checkboxRecordContinue_clicked(bool checked) { mRecordContinue = checked; updateUi(); } /*! * \brief Выполняется при изменении признака начала записи при запуске программы. * \param checked Значение признака. */ void ReplayWidget::on_checkboxRecordAtStartup_clicked(bool checked) { mRecordAtStartup = checked; updateUi(); } /*! * \brief Открывает файл для воспроизведения. */ void ReplayWidget::on_actionPlayFile_triggered() { QString fileName = QFileDialog::getOpenFileName(this, tr("Открыть файл записи"), QString(), mDescription + " (*." + mExtension + ")"); if (!fileName.isNull()) { openPlayFile(fileName); updatePlayStatus(); updateUi(); } } /*! * \brief Вызывает старт воспроизведения. */ void ReplayWidget::on_actionPlayStart_triggered() { beginPlay(); updatePlayStatus(); updateUi(); } /*! * \brief Приостанавливает чтение. */ void ReplayWidget::on_actionPlayPause_triggered() { pausePlay(); updatePlayStatus(); updateUi(); } /*! * \brief Производит остановку воспроизведения. */ void ReplayWidget::on_actionPlayStop_triggered() { endPlay(); updatePlayStatus(); updateUi(); } /*! * \brief Производит чтение порции информации из файла. */ void ReplayWidget::on_actionPlayForward_triggered() { playOnce(); updatePlayStatus(); updateUi(); if (mPlaying) { mTimeElapsed.restart(); slot_timerElapsed_timeout(); // Отобразить 0 секунд с момента предыдущего чтения. mTimerElapsed.start(); } } /*! * \brief Изменяет номер такта начала воспроизведения. * \param value Значение номера такта начала воспроизведения. */ void ReplayWidget::on_sliderPlayProgress_valueChanged(int value) { mPlayContext.setCursor(value); updatePlayStatus(); updateUi(); } /*! * \brief Изменяет скорость воспроизведения. * \param value Значение скорости воспроизведения. */ void ReplayWidget::on_sliderPlayRate_valueChanged(int value) { if (value <= 1000) { mPlayRate = value; } else { value = 1000 + (int)((value - 1000) / 10.) * 10; mPlayRate = (value - 1000) / (1900 - 1000.) * (10000 - 1000) + 1000; } mTimer.setInterval(mPlayRate); updatePlayStatus(); updateUi(); } /*! * \brief Вызывается таймером чтения. Производит чтение одной порции входной информации из файла. */ void ReplayWidget::slot_timer_timeout() { mTimerElapsed.stop(); playOnce(); updatePlayStatus(); updateUi(); if (mPlaying) { mTimeElapsed.restart(); slot_timerElapsed_timeout(); // Отобразить 0 секунд с момента предыдущего чтения. mTimerElapsed.start(); } } /*! * \brief Вызывается таймером отсчета времени. Обновляет отображение времени с момента предыдущего чтения. */ void ReplayWidget::slot_timerElapsed_timeout() { QString text; if (mPlayRate <= 1000) { text = tr("%1 c.").arg(mPlayRate / 1000., 0, 'f', 3); } else { text = tr("%1 c.").arg(mPlayRate / 1000., 0, 'f', 1); } ui->labelPlayRate->setText((text + " (%1)").arg(mTimeElapsed.elapsed() / 1000., 0, 'f', 1)); } /*! * \brief Открывает файл для записи, записывает служебную информацию. * \param fileName Имя файла. * \param play Признак воспроизведения после открытия файла. */ void ReplayWidget::openRecordFile(QString fileName, bool record) { //// Создание каталога для записи файлов входной информации. QDir dir(mRecordsDir); if (!dir.exists()) { dir.mkpath(dir.path()); } //// if (fileName.isNull()) { //// Начало записи. fileName = mRecordsDir + "/" + QDateTime::currentDateTime().toString("yyyyMMdd-hhmmss") + "." + mExtension; mRecordContinued = false; mRecordedPortionsTotal = 0; mRecordedBytesTotal = 0; mRecordedFilesTotal = 0; } if (!mRecordContext.open(fileName, mSignature, mVersion)) { endRecord(); QMessageBox::critical(this, tr("Запись/Воспроизведение"), tr("Ошибка записи файла."), QMessageBox::Ok); return; } if (record) { beginRecord(); updateUi(); } updateRecordStatus(); } /*! * \brief Начинает запись. */ void ReplayWidget::beginRecord() { mRecording = true; updateRecordStatus(); } /*! * \brief Возвращает признак записи. * \return признак записи. */ bool ReplayWidget::isRecording() const { return mRecording; } /*! * \brief Завершает запись. */ void ReplayWidget::endRecord() { mRecording = false; emit endWrite(mRecordContext.fileName()); mRecordContext.close(); } /*! * \brief Вызывается при необходимости продолжить запись в новый файл. */ void ReplayWidget::continueRecord() { mRecordedFilesTotal ++; //// Задание нового имени файла. Новое имя файла = текущие дата и время + суффикс-номер файла. QString fileName = mRecordsDir + "/" + QDateTime::currentDateTime().toString("yyyyMMdd-hhmmss") + "." + QString("%1").arg(mRecordedFilesTotal, 3, 10, QChar('0')) + "." + mExtension; mRecordContinued = true; openRecordFile(fileName); beginRecord(); updateRecordStatus(); updateUi(); } /*! * \brief Выполняет однократную запись порции информации. * \param data Массив входной информации. * \param length Длина массива. * \param time Время. * \param offsetUTC Смещение отностительно UTC [c]. */ void ReplayWidget::recordOnce(char *data, int64_t length, time_t time, long offsetUTC) { if (mRecording) { //// Файл создается при записи первой порции информации. if (mRecordContext.byteCount() == 0) { openRecordFile(); emit beginWrite(mRecordContext.fileName()); updateUi(); } //// Проверка ограничения на размер файла. if (mRecordLimitType == MBs) { int64_t nextBytes = mRecordContext.byteCount() + length; if (nextBytes > static_cast<int64_t>(mRecordLimitMBs) * 1024 * 1024) { endRecord(); updateUi(); if (!mRecordContinue) { QMessageBox *information = new QMessageBox(QMessageBox::Information, tr("Запись/Воспроизведение"), tr("Остановка записи в файл. Достигнут предел по размеру файла!"), QMessageBox::Ok); information->setAttribute(Qt::WA_DeleteOnClose, true); information->show(); return; } } } //// Проверка ограничения на количество порций информации. if (mRecordLimitType == Portions) { int nextBlocks = mRecordContext.portionCount() + 1; if (nextBlocks > mRecordLimitPortions) { endRecord(); updateUi(); if (!mRecordContinue) { QMessageBox *information = new QMessageBox(QMessageBox::Information, tr("Запись/Воспроизведение"), tr("Остановка записи в файл. Достигнут предел по количеству тактов!"), QMessageBox::Ok); information->setAttribute(Qt::WA_DeleteOnClose, true); information->show(); return; } } } //// Проверка необходимости начала нового файла записи. if (!mRecording && mRecordContinue) { continueRecord(); } //// try { recordPortion(data, length, time, offsetUTC); } catch (int) { endRecord(); QMessageBox::critical(this, tr("Запись/Воспроизведение"), tr("Ошибка записи файла."), QMessageBox::Ok); } updateRecordStatus(); updateUi(); } } /*! * \brief Выполняет запись порции информации. * \param data Массив входной информации. * \param length Длина массива. * \param time Время. * \param offsetUTC Смещение отностительно UTC [c]. */ void ReplayWidget::recordPortion(char *data, int64_t length, time_t time, long offsetUTC) { QElapsedTimer duration; duration.start(); bool result = mRecordContext.recordPortion(data, length, time, offsetUTC); double elapsed = duration.nsecsElapsed() / 1000000.; if (!result) { mRecordedBytes = 0; mRecordedMs = 0; throw 0; } else { mRecordedBytes = length; mRecordedMs = elapsed; } mRecordedBytesTotal += length + sizeof(Replay::PortionInfo); mRecordedPortionsTotal ++; } /*! * \brief Устанавливает вид элементов графического интерфейса, отображающих текущий размер записываемого файла. */ void ReplayWidget::updateRecordStatus() { ui->labelRecFile->setText(mRecordContext.fileName()); ui->labelRecFile->setToolTip(mRecordContext.fileName()); if (!mRecordContinued) { //// Количество порций. ui->labelRecPortions->setText(QString::number(mRecordContext.portionCount())); //// Количество байт. if (mRecordedBytesTotal < 1024) { ui->labelRecBytes->setText(QString::number(mRecordedBytesTotal) + tr(" Байт")); } if (1024 <= mRecordedBytesTotal && mRecordedBytesTotal < 1024 * 1024) { ui->labelRecBytes->setText(QString::number(mRecordedBytesTotal / 1024) + tr(" КБайт")); } if (1024 * 1024 <= mRecordedBytesTotal) { ui->labelRecBytes->setText(QString::number(mRecordedBytesTotal / (1024 * 1024)) + tr(" МБайт")); } } else { //// Количество порций. ui->labelRecPortions->setText(QString::number(mRecordContext.portionCount()) + " / " + QString::number(mRecordedPortionsTotal) + tr(" (Файлов: %1)").arg(mRecordedFilesTotal + 1)); //// Количество байт. QString bytesCount; if (mRecordContext.byteCount() < 1024) { bytesCount = QString::number(mRecordContext.byteCount()) + tr(" Байт"); } if (1024 <= mRecordContext.byteCount() && mRecordContext.byteCount() < 1024 * 1024) { bytesCount = QString::number(mRecordContext.byteCount() / 1024) + tr(" КБайт"); } if (1024 * 1024 <= mRecordContext.byteCount()) { bytesCount = QString::number(mRecordContext.byteCount() / (1024 * 1024)) + tr(" МБайт"); } QString bytesTotal; if (mRecordedBytesTotal < 1024) { bytesTotal = QString::number(mRecordedBytesTotal) + tr(" Байт"); } if (1024 <= mRecordedBytesTotal && mRecordedBytesTotal < 1024 * 1024) { bytesTotal = QString::number(mRecordedBytesTotal / 1024) + tr(" КБайт"); } if (1024 * 1024 <= mRecordedBytesTotal) { bytesTotal = QString::number(mRecordedBytesTotal / (1024 * 1024)) + tr(" МБайт"); } ui->labelRecBytes->setText(bytesCount + " / " + bytesTotal); } if (mRecordedBytes != 0) { QString text; if (mRecordedMs != 0) { text += tr("Длительность записи: %1 мс. ").arg(mRecordedMs, 0, 'f', 1); } QString bytes; if (mRecordedBytes < 1024) { bytes = QString::number(mRecordedBytes) + tr(" Байт"); } if (1024 <= mRecordedBytes && mRecordedBytes < 1024 * 1024) { bytes = QString::number(mRecordedBytes / 1024) + tr(" КБайт"); } if (1024 * 1024 <= mRecordedBytes) { bytes = QString::number(mRecordedBytes / (1024 * 1024)) + tr(" МБайт"); } text += tr("Записано ") + bytes; ui->labelRecordedBytes->setText(text); ui->labelRecordedBytes->setToolTip(text); } } /*! * \brief Устанавливает темп воспроизведения [мс]. * \param playRate Темп воспроизведения [мс] */ void ReplayWidget::setPlayRate(int playRate) { if (playRate < PlayRateMax) { playRate = PlayRateMax; } if (playRate > PlayRateMin) { playRate = PlayRateMin; } mPlayRate = playRate; updateUi(); } /*! * \brief Открывает файл для чтения, читает служебную информацию. * \param fileName Имя файла. * \param play Признак воспроизведения после открытия файла. * \param rate Темп воспроизведения [мс]. Если -1, то значение не задано. Если !play, то значение не используется. */ void ReplayWidget::openPlayFile(QString fileName, bool play, int rate) { mPlayContext.close(); QString signature = mSignature; int version = mVersion; if (!mPlayContext.open(fileName, signature, version)) { if (mSignature != signature) { QMessageBox::critical(this, tr("Запись/Воспроизведение"), tr("Ошибка сигнатуры."), QMessageBox::Ok); return; } if (mVersion != version) { QMessageBox::critical(this, tr("Запись/Воспроизведение"), tr("Ошибка версии. Текщая версия - %1, версия файла - %2.").arg(mVersion).arg(version), QMessageBox::Ok); return; } QMessageBox::critical(this, tr("Запись/Воспроизведение"), tr("Ошибка открытия файла."), QMessageBox::Ok); return; } if (mPlayContext.portionCount() == 0) { QMessageBox::information(this, tr("Воспроизведение"), tr("Нет данных для воспроизведения!")); } if (play) { if (rate != -1) { setPlayRate(rate); } beginPlay(); } } /*! * \brief Начинает воспроизведение. */ void ReplayWidget::beginPlay() { ui->tabWidget->setCurrentIndex(1); mPlaying = true; emit beginRead(mPlayContext.fileName()); if (!mPlayPause) { mTimer.start(mPlayRate); slot_timer_timeout(); // Выполнить итерацию сразу. } } /*! * \brief Приостаналивает воспроизведение. */ void ReplayWidget::pausePlay() { if (mPlaying && !mPlayPause) { mTimer.stop(); // Для избежания воспроизведения такта по истечении таймаута. } mPlayPause = !mPlayPause; if (mPlaying && !mPlayPause) { mTimer.start(mPlayRate); slot_timer_timeout(); // Выполнить итерацию сразу. } } /*! * \brief Завершает воспроизведение. */ void ReplayWidget::endPlay() { mPlaying = false; mPlayPause = false; mTimer.stop(); mTimerElapsed.stop(); emit endRead(mPlayContext.fileName()); mPlayContext.setCursor(0); } /*! * \brief Вызывается при возникновении исключения во время чтения. */ void ReplayWidget::abortPlay() { QMessageBox::critical(this, tr("Запись/Воспроизведение"), tr("Ошибка воспроизведения файла."), QMessageBox::Ok); endPlay(); } /*! * \brief Выполняет однократное воспроизведение порции информации. */ void ReplayWidget::playOnce() { try { playPortion(); if (mPlaying && mPlayContext.cursor() == mPlayContext.portionCount()) { endPlay(); } } catch (int) { abortPlay(); } } /*! * \brief Выполняет воспроизведение порции информации. */ void ReplayWidget::playPortion() { auto data = acquireInputBuffer(); int64_t length = 0; QElapsedTimer duration; duration.start(); bool result = mPlayContext.playPortion(data.get(), length); quint64 elapsed = duration.elapsed(); if (!result) { mPlayedBytes = 0; mPlayedMs = 0; releaseInputBuffer(data); throw 0; } else { mPlayedBytes = length; mPlayedMs = elapsed; } releaseInputData(data); emit endReadPortion(); } /*! * \brief Устанавливает вид элементов графического интерфейса, отображающих количество прочитанных порций входной информации. */ void ReplayWidget::updatePlayStatus() { ui->labelPlayFile->setText(mPlayContext.fileName()); ui->labelPlayFile->setToolTip(mPlayContext.fileName()); if (mPlayContext.portionCount() != 0) { ui->labelPlayPortions->setText(tr("%1 из %2").arg(mPlayContext.cursor()).arg(mPlayContext.portionCount())); if (mPlayContext.cursor() != 0) { ui->labelPlayTime->setText(toModifiedTime(QDateTime::fromTime_t(mPlayContext.previousPortion().time)).toString("dd.MM.yyyy - hh:mm:ss")); } else { ui->labelPlayTime->setText(toModifiedTime(QDateTime::fromTime_t(mPlayContext.currentPortion().time)).toString("dd.MM.yyyy - hh:mm:ss")); } } else { ui->labelPlayPortions->setText(""); ui->labelPlayTime->setText(""); } ui->sliderPlayProgress->blockSignals(true); ui->sliderPlayProgress->setMinimum(0); ui->sliderPlayProgress->setMaximum(mPlayContext.portionCount() - 1); ui->sliderPlayProgress->setValue(mPlayContext.cursor()); ui->sliderPlayProgress->blockSignals(false); if (mPlayedBytes != 0) { QString text; if (mPlayedMs != 0) { text += tr("Длительность чтения: %1 мс. ").arg(mPlayedMs, 0, 'f', 1); } text += tr("Прочитано байт: %1").arg(mPlayedBytes); ui->labelPlayedBytes->setText(text); ui->labelPlayedBytes->setToolTip(text); } } /*! * \brief Изменение вручную позиции прогресса воспроизведения файла. */ void ReplayWidget::on_lineEditPortions_editingFinished() { QString text = ui->lineEditPortions->text(); bool ok; int value = text.toInt(&ok); if (ok) { if (0 <= value && value < mPlayContext.portionCount() - 1) { mPlayContext.setCursor(value); updatePlayStatus(); } } ui->lineEditPortions->setVisible(false); } /*! * \brief Выполняется при нажатии кнопки выбора директории для записи. */ void ReplayWidget::on_pushButtonChooseDir_clicked() { QString directory = QFileDialog::getExistingDirectory(this, "Выберите директорию", mRecordsDir, QFileDialog::ShowDirsOnly); if (!directory.isEmpty()) { mRecordsDir = directory; } }