]> git.sesse.net Git - pkanalytics/commitdiff
Begin showing events from the database. (No modification yet.)
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sun, 30 Apr 2023 21:53:58 +0000 (23:53 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Sun, 30 Apr 2023 21:53:58 +0000 (23:53 +0200)
mainwindow.h
mainwindow.ui
meson.build
stats.cpp

index 052e15d8b45cbc2b47491b46d87a4c67706c7c18..5f2188c1f1acdc2b4b1a3352371dbcf546827e83 100644 (file)
@@ -3,6 +3,9 @@
 #include <QApplication>
 #include <stdint.h>
 #include <optional>
+#include "ui_mainwindow.h"
+
+class QAbstractItemModel;
 
 class MainWindow : public QMainWindow
 {
@@ -10,12 +13,14 @@ class MainWindow : public QMainWindow
 
 public:
        MainWindow();
-
-       QMediaPlayer *player;
+       void setModel(QAbstractItemModel *model);
 
 private:
        void seek(int64_t delta_ms);
+
+       Ui::MainWindow *ui;
        bool seeking = false;
        bool playing = true;
        std::optional<uint64_t> buffered_seek;
+       QMediaPlayer *player;
 };
index a64b1e380cdb3063e2f54692f41bfa044d29a75e..c95640fa125ea6e3891e48d8ac33b00187e902a0 100644 (file)
@@ -35,7 +35,7 @@
        </widget>
       </item>
       <item row="2" column="0">
-       <widget class="QTableView" name="tableView"/>
+       <widget class="QTableView" name="event_view"/>
       </item>
      </layout>
     </item>
index 4df5b37df99b11c50ba3e6707cc13328fd0a2506..dd159e1f3f26e3e557c498f45e3e6ec88397e288 100644 (file)
@@ -2,9 +2,11 @@ project('stats', 'cpp', default_options: ['buildtype=debugoptimized'], version:
 
 qt6 = import('qt6')
 qt6deps = dependency('qt6', modules: ['Core', 'Gui', 'Widgets', 'Multimedia', 'MultimediaWidgets'])
+sqlite3dep = dependency('sqlite3')
+
 qt_files = qt6.preprocess(
         moc_headers: ['mainwindow.h'],
        ui_files: ['mainwindow.ui'],
         dependencies: qt6deps)
 
-executable('stats', 'stats.cpp', qt_files, dependencies: [qt6deps])
+executable('stats', 'stats.cpp', qt_files, dependencies: [qt6deps, sqlite3dep])
index 1edee1161b2d022de75aa9f690a5aab6f54ede2e..a740c1df9d452edf7a85ac6b3b3913e68c89375f 100644 (file)
--- a/stats.cpp
+++ b/stats.cpp
@@ -5,10 +5,15 @@
 #include <QVideoWidget>
 #include <QShortcut>
 #include <string>
+#include <map>
+#include <vector>
+#include <optional>
 #include <sqlite3.h>
 #include "mainwindow.h"
 #include "ui_mainwindow.h"
 
+using namespace std;
+
 std::string format_timestamp(uint64_t pos)
 {
        int ms = pos % 1000;
@@ -23,6 +28,150 @@ std::string format_timestamp(uint64_t pos)
        return buf;
 }
 
+class EventsModel : public QAbstractTableModel
+{
+public:
+       EventsModel(sqlite3 *db) : db(db) {}
+
+       int rowCount(const QModelIndex &parent) const override
+       {
+               refresh_if_needed();
+               return events.size();
+       }
+       int columnCount(const QModelIndex &column) const override
+       {
+               return 3;
+       }
+       QVariant headerData(int section, Qt::Orientation orientation, int role) const override
+       {
+               if (role != Qt::DisplayRole) {
+                       return QVariant();
+               }
+               if (orientation == Qt::Horizontal) {
+                       if (section == 0) {
+                               return "Time";
+                       } else if (section == 1) {
+                               return "Player";
+                       } else {
+                               return "Type";
+                       }
+               } else {
+                       return "";
+               }
+       }
+
+       QVariant data(const QModelIndex &index, int role) const override
+       {
+               if (role != Qt::DisplayRole) {
+                       return QVariant();
+               }
+               refresh_if_needed();
+               if (index.column() == 0) {
+                       return QString::fromUtf8(format_timestamp(events[index.row()].t));
+               } else if (index.column() == 1) {
+                       optional<int> player_id = events[index.row()].player_id;
+                       if (player_id) {
+                               const Player &p = players[*player_id];
+                               return QString::fromUtf8(p.name + " (" + p.number + ")");
+                       } else {
+                               return QVariant();
+                       }
+               } else if (index.column() == 2) {
+                       return QString::fromUtf8(events[index.row()].type);
+               }
+               return QVariant();
+       }
+
+private:
+       struct Player {
+               int player_id;
+               string number;
+               string name;
+       };
+       mutable map<int, Player> players;
+
+       struct Event {
+               uint64_t t;
+               optional<int> player_id;
+               string type;
+       };
+       mutable vector<Event> events;
+       mutable bool stale = true;
+
+       sqlite3 *db;
+
+       void refresh_if_needed() const;
+};
+
+void EventsModel::refresh_if_needed() const
+{
+       if (!stale) {
+               return;
+       }
+
+       players.clear();
+       events.clear();
+       stale = false;
+
+       // Read the players.
+       sqlite3_stmt *stmt;
+       int ret = sqlite3_prepare_v2(db, "SELECT player, number, name FROM player", -1, &stmt, 0);
+       if (ret != SQLITE_OK) {
+               fprintf(stderr, "SELECT prepare: %s\n", sqlite3_errmsg(db));
+               abort();
+       }
+       for ( ;; ) {
+               ret = sqlite3_step(stmt);
+               if (ret == SQLITE_ROW) {
+                       Player p;
+                       p.player_id = sqlite3_column_int(stmt, 0);
+                       p.number = (const char *)sqlite3_column_text(stmt, 1);
+                       p.name = (const char *) sqlite3_column_text(stmt, 2);
+                       players[p.player_id] = move(p);
+               } else if (ret == SQLITE_DONE) {
+                       break;
+               } else {
+                       fprintf(stderr, "SELECT step: %s\n", sqlite3_errmsg(db));
+                       abort();
+               }
+       }
+       ret = sqlite3_finalize(stmt);
+       if (ret != SQLITE_OK) {
+               fprintf(stderr, "SELECT finalize: %s\n", sqlite3_errmsg(db));
+               abort();
+       }
+
+       // Read the events.
+       ret = sqlite3_prepare_v2(db, "SELECT t, player, type FROM event", -1, &stmt, 0);
+       if (ret != SQLITE_OK) {
+               fprintf(stderr, "SELECT prepare: %s\n", sqlite3_errmsg(db));
+               abort();
+       }
+       for ( ;; ) {
+               ret = sqlite3_step(stmt);
+               if (ret == SQLITE_ROW) {
+                       Event e;
+                       e.t = sqlite3_column_int(stmt, 0);
+                       e.player_id = sqlite3_column_int(stmt, 1);
+                       e.type = (const char *)sqlite3_column_text(stmt, 2);
+                       events.push_back(move(e));
+               } else if (ret == SQLITE_DONE) {
+                       break;
+               } else {
+                       fprintf(stderr, "SELECT step: %s\n", sqlite3_errmsg(db));
+                       abort();
+               }
+       }
+       ret = sqlite3_finalize(stmt);
+       if (ret != SQLITE_OK) {
+               fprintf(stderr, "SELECT finalize: %s\n", sqlite3_errmsg(db));
+               abort();
+       }
+
+       // TODO what if data changes externally?
+       //emit dataChanged(QModelIndex(
+}
+
 MainWindow::MainWindow()
 {
        player = new QMediaPlayer;
@@ -30,10 +179,10 @@ MainWindow::MainWindow()
        player->setSource(QUrl::fromLocalFile("/home/sesse/dev/stats/ultimate-prores.mkv"));
        player->play();
 
-       Ui::MainWindow *ui = new Ui::MainWindow;
+       ui = new Ui::MainWindow;
        ui->setupUi(this);
 
-       connect(player, &QMediaPlayer::positionChanged, [ui, this](uint64_t pos) {
+       connect(player, &QMediaPlayer::positionChanged, [this](uint64_t pos) {
                ui->timestamp->setText(QString::fromUtf8(format_timestamp(pos)));
                if (buffered_seek) {
                        player->setPosition(*buffered_seek);
@@ -77,6 +226,11 @@ MainWindow::MainWindow()
        });
 }
 
+void MainWindow::setModel(QAbstractItemModel *model)
+{
+       ui->event_view->setModel(model);
+}
+
 void MainWindow::seek(int64_t delta_ms)
 {
        int64_t current_pos = buffered_seek ? *buffered_seek : player->position();
@@ -88,11 +242,35 @@ void MainWindow::seek(int64_t delta_ms)
        }
 }
 
+sqlite3 *open_db(const char *filename)
+{
+       sqlite3 *db;
+       int ret = sqlite3_open(filename, &db);
+       if (ret != SQLITE_OK) {
+               fprintf(stderr, "%s: %s\n", filename, sqlite3_errmsg(db));
+               exit(1);
+       }
+
+       sqlite3_exec(db, R"(
+               CREATE TABLE IF NOT EXISTS player (player INTEGER PRIMARY KEY, number VARCHAR, name VARCHAR);
+       )", nullptr, nullptr, nullptr);  // Ignore errors.
+
+       sqlite3_exec(db, R"(
+               CREATE TABLE IF NOT EXISTS event (t INTEGER, player INTEGER, type VARCHAR, FOREIGN KEY (player) REFERENCES player(player));
+       )", nullptr, nullptr, nullptr);  // Ignore errors.
+
+       sqlite3_exec(db, "PRAGMA journal_mode=WAL", nullptr, nullptr, nullptr);  // Ignore errors.
+       sqlite3_exec(db, "PRAGMA synchronous=NORMAL", nullptr, nullptr, nullptr);  // Ignore errors.
+       return db;
+}
+
 int main(int argc, char *argv[])
 {
        QApplication app(argc, argv);
+       sqlite3 *db = open_db("ultimate.db");
 
        MainWindow mainWindow;
+       mainWindow.setModel(new EventsModel(db));
        mainWindow.resize(QSize(1280, 720));
        mainWindow.show();