X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=main.cpp;h=95ecdb95ee83f11f53380dde71215c776ac2b62a;hb=d9d1ea8099961060f8ec41c7009ba3d8d2c3d333;hp=b65895d38a278b2edff0bc9b614a231eac5db5d8;hpb=b0bbddfe5b054eb9d40fa8b12d9f5bf86dd6dcee;p=pkanalytics diff --git a/main.cpp b/main.cpp index b65895d..95ecdb9 100644 --- a/main.cpp +++ b/main.cpp @@ -13,6 +13,7 @@ #include "mainwindow.h" #include "ui_mainwindow.h" #include "events.h" +#include "players.h" using namespace std; @@ -30,12 +31,12 @@ string format_timestamp(uint64_t pos) return buf; } -MainWindow::MainWindow(EventsModel *events) : events(events) +MainWindow::MainWindow(EventsModel *events, PlayersModel *players) : events(events), players(players) { - player = new QMediaPlayer; - //player->setSource(QUrl::fromLocalFile("/home/sesse/dev/stats/ultimate.mkv")); - player->setSource(QUrl::fromLocalFile("/home/sesse/dev/stats/ultimate-prores.mkv")); - player->play(); + video = new QMediaPlayer; + //video->setSource(QUrl::fromLocalFile("/home/sesse/dev/stats/ultimate.mkv")); + video->setSource(QUrl::fromLocalFile("/home/sesse/dev/stats/ultimate-prores.mkv")); + video->play(); ui = new Ui::MainWindow; ui->setupUi(this); @@ -43,14 +44,16 @@ MainWindow::MainWindow(EventsModel *events) : events(events) ui->event_view->setModel(events); connect(ui->event_view->selectionModel(), &QItemSelectionModel::currentRowChanged, [this, events](const QModelIndex ¤t, const QModelIndex &previous) { - player->setPosition(events->get_time(current.row())); + video->setPosition(events->get_time(current.row())); }); - connect(player, &QMediaPlayer::positionChanged, [this](uint64_t pos) { + ui->player_view->setModel(players); + + connect(video, &QMediaPlayer::positionChanged, [this](uint64_t pos) { position_changed(pos); }); - player->setVideoOutput(ui->video); + video->setVideoOutput(ui->video); connect(ui->minus10s, &QPushButton::clicked, [this]() { seek(-10000); }); connect(ui->plus10s, &QPushButton::clicked, [this]() { seek(10000); }); @@ -64,11 +67,11 @@ MainWindow::MainWindow(EventsModel *events) : events(events) connect(ui->play_pause, &QPushButton::clicked, [this]() { if (playing) { - player->pause(); + video->pause(); ui->play_pause->setText("Play (space)"); } else { - player->setPlaybackRate(1.0); - player->play(); + video->setPlaybackRate(1.0); + video->play(); ui->play_pause->setText("Pause (space)"); } playing = !playing; @@ -85,6 +88,7 @@ MainWindow::MainWindow(EventsModel *events) : events(events) connect(ui->player_6, &QPushButton::clicked, [this]() { insert_event(6); }); connect(ui->player_7, &QPushButton::clicked, [this]() { insert_event(7); }); + // Offensive events // TODO: disable if nothing is selected connect(ui->catch_, &QPushButton::clicked, [this]() { set_current_event_type("catch"); }); connect(ui->throwaway, &QPushButton::clicked, [this]() { set_current_event_type("throwaway"); }); @@ -93,7 +97,20 @@ MainWindow::MainWindow(EventsModel *events) : events(events) connect(ui->offensive_soft_plus, &QPushButton::clicked, [this]() { set_current_event_type("offensive_soft_plus"); }); connect(ui->offensive_soft_minus, &QPushButton::clicked, [this]() { set_current_event_type("offensive_soft_minus"); }); connect(ui->pull, &QPushButton::clicked, [this]() { set_current_event_type("pull"); }); - connect(ui->pull_landed, &QPushButton::clicked, [this]() { set_current_event_type("pull_landed"); }); + connect(ui->pull_landed, &QPushButton::clicked, [this]() { insert_noplayer_event("pull_landed"); }); + + // Defensive events (TODO add more) + connect(ui->their_throwaway, &QPushButton::clicked, [this]() { insert_noplayer_event("their_throwaway"); }); + connect(ui->their_goal, &QPushButton::clicked, [this]() { insert_noplayer_event("their_goal"); }); + connect(ui->their_pull, &QPushButton::clicked, [this]() { insert_noplayer_event("their_pull"); }); + connect(ui->our_defense, &QPushButton::clicked, [this]() { set_current_event_type("defense"); }); // TODO: player-connected + connect(ui->defensive_soft_plus, &QPushButton::clicked, [this]() { set_current_event_type("defensive_soft_plus"); }); // TODO: player-connected + connect(ui->defensive_soft_minus, &QPushButton::clicked, [this]() { set_current_event_type("defensive_soft_minus"); }); // TODO: player-connected + + // Misc. events + connect(ui->substitution, &QPushButton::clicked, [this]() { make_substitution(); }); + connect(ui->stoppage, &QPushButton::clicked, [this]() { insert_noplayer_event("stoppage"); }); // FIXME needs a way to restart + connect(ui->unknown, &QPushButton::clicked, [this]() { insert_noplayer_event("unknown"); }); QShortcut *key_delete = new QShortcut(QKeySequence(Qt::Key_Delete), this); connect(key_delete, &QShortcut::activated, [this]() { ui->delete_->animateClick(); }); @@ -104,30 +121,53 @@ void MainWindow::position_changed(uint64_t pos) { ui->timestamp->setText(QString::fromUtf8(format_timestamp(pos))); if (buffered_seek) { - player->setPosition(*buffered_seek); + video->setPosition(*buffered_seek); buffered_seek.reset(); } if (!playing) { - player->pause(); // We only played to get a picture. + video->pause(); // We only played to get a picture. } update_status(); } void MainWindow::seek(int64_t delta_ms) { - int64_t current_pos = buffered_seek ? *buffered_seek : player->position(); + int64_t current_pos = buffered_seek ? *buffered_seek : video->position(); uint64_t pos = max(current_pos + delta_ms, 0); buffered_seek = pos; if (!playing) { - player->setPlaybackRate(0.01); - player->play(); // Or Qt won't show the seek. + video->setPlaybackRate(0.01); + video->play(); // Or Qt won't show the seek. } } void MainWindow::insert_event(int button_id) { + uint64_t t = video->position(); + set team = events->get_team_at(t); + if (button_id > team.size()) { + return; + } + int player_id = *next(team.begin(), button_id - 1); + + EventsModel::Status s = events->get_status_at(t); + ui->event_view->selectionModel()->blockSignals(true); - ui->event_view->selectRow(events->insert_event(player->position(), button_id)); + if (s.offense) { + // TODO: Perhaps not if that player already did the last catch? + ui->event_view->selectRow(events->insert_event(t, player_id, "catch")); + } else { + ui->event_view->selectRow(events->insert_event(t, player_id)); + } + ui->event_view->selectionModel()->blockSignals(false); +} + +void MainWindow::insert_noplayer_event(const string &type) +{ + uint64_t t = video->position(); + + ui->event_view->selectionModel()->blockSignals(true); + ui->event_view->selectRow(events->insert_event(t, nullopt, type)); ui->event_view->selectionModel()->blockSignals(false); } @@ -155,13 +195,75 @@ void MainWindow::delete_current_event() update_status(); } +void MainWindow::make_substitution() +{ + QItemSelectionModel *select = ui->player_view->selectionModel(); + + // FIXME: we should backdate t to start of point (last goal, or 0) if: + // - no players we're removing have had actions yet + // - there have been no other in/out events + // + // ...but if so, we might need to modify in/out events that are already there + // (perhaps just overwrite them all?) + uint64_t t = video->position(); + + set old_team = events->get_team_at(t); + set new_team; + + for (QModelIndex row : select->selectedRows()) { + new_team.insert(players->get_player_id(row.row())); + } + + for (int player_id : old_team) { + if (!new_team.count(player_id)) { + events->insert_event(t, player_id, "out"); + } + } + for (int player_id : new_team) { + if (!old_team.count(player_id)) { + events->insert_event(t, player_id, "in"); + } + } +} + void MainWindow::update_status() { - EventsModel::Status s = events->get_status_at(player->position()); + uint64_t t = video->position(); + EventsModel::Status s = events->get_status_at(t); char buf[256]; snprintf(buf, sizeof(buf), "%d–%d | %s | %d passes, %d sec possession", s.our_score, s.their_score, s.offense ? "offense" : "defense", s.num_passes, s.possession_sec); ui->status->setText(buf); + + // FIXME: sort by number, instead of by internal ID + QPushButton *buttons[] = { + ui->player_1, + ui->player_2, + ui->player_3, + ui->player_4, + ui->player_5, + ui->player_6, + ui->player_7 + }; + const char shortcuts[] = "qweasdf"; + int num_players = 0; + for (int player_id : events->get_team_at(t)) { + QPushButton *btn = buttons[num_players]; + string label = players->get_player_name_by_id(player_id) + " (&" + shortcuts[num_players] + ")"; + char shortcut[2] = ""; + shortcut[0] = toupper(shortcuts[num_players]); + btn->setText(QString::fromUtf8(label)); + btn->setShortcut(QCoreApplication::translate("MainWindow", shortcut, nullptr)); + btn->setEnabled(true); + if (++num_players == 7) { + break; + } + } + for (int i = num_players; i < 7; ++i) { + QPushButton *btn = buttons[i]; + btn->setText("No player"); + btn->setEnabled(false); + } } sqlite3 *open_db(const char *filename) @@ -191,7 +293,7 @@ int main(int argc, char *argv[]) QApplication app(argc, argv); sqlite3 *db = open_db("ultimate.db"); - MainWindow mainWindow(new EventsModel(db)); + MainWindow mainWindow(new EventsModel(db), new PlayersModel(db)); mainWindow.resize(QSize(1280, 720)); mainWindow.show();