Разработка для SailfishOS: таймеры и реализация экспорта в файл

Мы уже писали про опыт разработки нашего первого приложения для мобильной платформы Sailfish OS. Но на этом решили не останавливаться и сразу взялись за второе. Целью было создать приложение, с помощью которого пользователь мог бы вести учёт своего рабочего времени, планировать задачи и предоставлять информацию о проделанной работе, иными словами — разработать персональный мобильный тайм-трекер.

Описание приложения


Приложение, естественно, должно уметь вести журнал задач с возможность сохранения времени, потраченного на них. Желательно было, чтобы время можно было засекать с помощью встроенного таймера, а не только прописывать руками. Кроме этого, планировалась возможность создание текстовых отчетов о проделанной работе, в виде таблиц, с последующей отправкой их работодателю.

Так как разработка самой структуры приложения и его интерфейса уже была описана в предыдущих статьях цикла разработки для Sailfish OS (путь от начала разработки до создания первого приложения вы можете отследить здесь и тут), то в этой статье будет описан только функционал, который отличается от реализованного ранее и наиболее интересен с точки зрения его реализации. В нашем приложении — это таймеры задач и экспорт списка задач.

Таймер


Для измерения длительности работ в приложении используется механизм таймера. Процесс отсчета времени осуществляется с помощью стандартного элемента Qt Timer, у которого выставлен интервал обновления 1000 миллисекунд.
Timer {
  id: stopwatch
  interval: 1000
  repeat: true
  running: true
  triggeredOnStart: true
  onTriggered: {
    if (timerActive) {
      var currentTime = new Date ();
      var differeceInTime = (currentTime.getTime() - previousTime.getTime());
      previousTime = currentTime;
      updateData(differentInTime);
    }
  }
}

function updateData(usec) {
  elapsedTime += usec;
  taskTimerString = getTimeString(elapsedTime);
}

При каждом срабатывании события onTriggered у Timer происходит вычисление времени таймера: из текущего времени вычитается время предыдущего срабатывания. Такая реализация удобна тем, что при выходе устройства из неактивного режима, время корректно будет обновлятся.

Для управления таймером сделаны кнопки Старт и Сбросить. Так же есть возможность поставить активный таймер на паузу. Чтобы сохранить замеренное время достаточно нажать на кнопку Сохранить время, доступную в выпадающем меню. Замер таймера автоматически добавится к текущему времени, потраченному на задачу.

d223e7f167bb495eb093430b4d7f8ea8.png

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

5607ecc1444444b5928159621f475f9a.png

Также, если таймер активен, то он отображается на обложке приложения.

Экспорт отчетов


5f976c43ef104c52b19e489b6677070b.png

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

function selectByPeriod(beginning, end) {
  var database = getDatabase();
  queryResult.clear();
  database.transaction(function(transaction) {
    var tasks = transaction.executeSql('SELECT * FROM tasks WHERE startDate >= ? AND finishDate <= ?',  [beginning, end]);
    for (var i = 0; i < tasks.rows.length; i++) {
      var element = tasks.rows.item(i);
      var startDate = new Date (element.startDate);
      var finishDate = new Date (element.finishDate);
      convertDateToUTC(startDate);
      convertDateToUTC(finishDate);
      var idDone = element.taskDone === 0 ? false : true
      queryResult.append({"startDate": startDate, "finishDate": finishDate, "taskName": element.taskName, "taskDescription": element.taskDescription, "taskDone": isDone, "spentTime": element.spentTime});
    }
  })
} 

Есть возможность записывать отчеты в файлы двух типов: csv и html. Выбор формата исходного файла происходит также на экране создания отчетов. Для каждого из типов реализован c++ класс, отвечающий за создание соответствующего файла. Запись в csv файлы осуществляется с помощью текстового потока QTextStream. Для корректного отображения кириллицы используется кодировка Windows-1251.
Q_INVOKABLE void writeLine(QVariantList taskInfo) {
    QTextCodec *utf8 = QTextCodec::codecForName("Windows-1251");
    QTextStream stream(&csvFile);
    QStringList line;
    stream.setCodec(utf8);
    for(int i = 0; i < taskInfo.size(); i++) {
        line << taskInfo[i].toString();
    }
    stream << line.join(",") << endl;
}

HTML отчеты создаются с помощью класса QTextDocument. Для создания таблицы с информацией по задачам используется класс QTextTable. Он позволяет задать стиль таблицы и текста в ней.
Q_INVOKABLE void createDocument(int rows, QVariantList columns) {
    report = new QTextDocument();
    QTextCursor cursor(report);
    table = cursor.insertTable(rows + 1, columns.length());
    QTextCharFormat format;
    format.setFontWeight(QFont::Bold);
    QTextCharFormat cellFormat;
    cellFormat.setBackground(QBrush(Qt::cyan));
    for(int col = 0; col < table->columns(); col++) {
        QTextTableCell cell = table->cellAt(0, col);
        QTextCursor cellCursor = cell.firstCursorPosition();
        cell.setFormat(cellFormat);
        cellCursor.mergeCharFormat(format);
        cellCursor.insertText(columns[col].toString());
    }
    QTextTableFormat tableFormat = cursor.currentTable()->format();
    tableFormat.setCellSpacing(0);
    table->setFormat(tableFormat);
}

Q_INVOKABLE void addRow(int row, QVariantList taskInfo) {
    QTextCharFormat cellFormat;
    cellFormat.setBackground(QBrush(Qt::darkCyan));
    for(int col = 0; col < table->columns(); col++) {
        QTextTableCell cell = table->cellAt(row, col);
        QTextCursor cellCursor = cell.firstCursorPosition();
        cellCursor.insertText(taskInfo[col].toString());
    }
    QTextTableCell indexCell = table->cellAt(row, 0);
    indexCell.setFormat(cellFormat);
}

Заключение


В результате было создано приложение с понятным и простым интерфейсом, которое позволяет легко вести и отслеживать список дел. Из ранее не планируемого функционала была добавлена возможность фильтровать задачи по статусу выполнения, чтобы можно было отсеивать выполненные. Приложение было опубликовано в магазине приложений Jolla Harbour под названием Report Card и доступно для скачивания всем желающим. Исходники приложения доступны на GitHub.

Автор: Максим Костерин

Комментарии (0)

© Habrahabr.ru