Improve performance of Fox and contest log view windows

This include inverting  the order of table view rows  so the newest is
at the top, without that the Qt MVC interactions when using a database
table based model is too slow and complex to manage.

The table views  now have sort by column capability  in the normal way
(click column  header to  reverse sort order)  for timely  logging and
non-disruption  of  Tx  starts  the  log  view  should  be  sorted  in
descending time order and scrolled to the last row added. Without that
Fox and  contest logging will work  but serious delays may  be invoked
that disrupt operation.
This commit is contained in:
Bill Somerville 2018-12-06 05:41:16 +00:00
parent 1c48b39b58
commit b8e4517718
20 changed files with 133 additions and 63 deletions

View File

@ -28,3 +28,4 @@ void CallsignDelegate::setModelData (QWidget * editor, QAbstractItemModel * mode
{ {
model->setData (index, static_cast<QLineEdit *> (editor)->text (), Qt::EditRole); model->setData (index, static_cast<QLineEdit *> (editor)->text (), Qt::EditRole);
} }

View File

@ -6,6 +6,8 @@
#include <locale.h> #include <locale.h>
#include <fftw3.h> #include <fftw3.h>
#include <QSharedMemory>
#include <QTemporaryFile>
#include <QDateTime> #include <QDateTime>
#include <QApplication> #include <QApplication>
#include <QRegularExpression> #include <QRegularExpression>
@ -21,6 +23,7 @@
#include <QCommandLineParser> #include <QCommandLineParser>
#include <QCommandLineOption> #include <QCommandLineOption>
#include <QSqlDatabase> #include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError> #include <QSqlError>
#include "revision_utils.hpp" #include "revision_utils.hpp"
@ -288,6 +291,12 @@ int main(int argc, char *argv[])
throw std::runtime_error {("Database Error: " + db.lastError ().text ()).toStdString ()}; throw std::runtime_error {("Database Error: " + db.lastError ().text ()).toStdString ()};
} }
// better performance traded for a risk of d/b corruption
// on system crash or application crash
// db.exec ("PRAGMA synchronous=OFF"); // system crash risk
// db.exec ("PRAGMA journal_mode=MEMORY"); // application crash risk
db.exec ("PRAGMA locking_mode=EXCLUSIVE");
int result; int result;
do do
{ {

View File

@ -59,6 +59,12 @@ CabrilloLog::impl::impl (Configuration const * configuration)
setHeaderData (fieldIndex ("exchange_sent"), Qt::Horizontal, tr ("Sent")); setHeaderData (fieldIndex ("exchange_sent"), Qt::Horizontal, tr ("Sent"));
setHeaderData (fieldIndex ("exchange_rcvd"), Qt::Horizontal, tr ("Rcvd")); setHeaderData (fieldIndex ("exchange_rcvd"), Qt::Horizontal, tr ("Rcvd"));
setHeaderData (fieldIndex ("band"), Qt::Horizontal, tr ("Band")); setHeaderData (fieldIndex ("band"), Qt::Horizontal, tr ("Band"));
// This descending order by time is important, it makes the view
// place the latest row at the top, without this the model/view
// interactions are both sluggish and unhelpful.
setSort (fieldIndex ("when"), Qt::DescendingOrder);
SQL_error_check (*this, &QSqlTableModel::select); SQL_error_check (*this, &QSqlTableModel::select);
} }
@ -113,11 +119,11 @@ bool CabrilloLog::add_QSO (Frequency frequency, QDateTime const& when, QString c
{ {
m_->revert (); // discard any uncommitted changes m_->revert (); // discard any uncommitted changes
} }
m_->setEditStrategy (QSqlTableModel::OnManualSubmit);
ConditionalTransaction transaction {*m_};
auto ok = m_->insertRecord (-1, record); auto ok = m_->insertRecord (-1, record);
if (ok) transaction.submit ();
{ m_->setEditStrategy (QSqlTableModel::OnFieldChange);
m_->select (); // to refresh views
}
return ok; return ok;
} }

View File

@ -51,6 +51,12 @@ FoxLog::impl::impl ()
setHeaderData (fieldIndex ("report_sent"), Qt::Horizontal, tr ("Sent")); setHeaderData (fieldIndex ("report_sent"), Qt::Horizontal, tr ("Sent"));
setHeaderData (fieldIndex ("report_rcvd"), Qt::Horizontal, tr ("Rcvd")); setHeaderData (fieldIndex ("report_rcvd"), Qt::Horizontal, tr ("Rcvd"));
setHeaderData (fieldIndex ("band"), Qt::Horizontal, tr ("Band")); setHeaderData (fieldIndex ("band"), Qt::Horizontal, tr ("Band"));
// This descending order by time is important, it makes the view
// place the latest row at the top, without this the model/view
// interactions are both sluggish and unhelpful.
setSort (fieldIndex ("when"), Qt::DescendingOrder);
SQL_error_check (*this, &QSqlTableModel::select); SQL_error_check (*this, &QSqlTableModel::select);
} }
@ -104,11 +110,14 @@ bool FoxLog::add_QSO (QDateTime const& when, QString const& call, QString const&
{ {
m_->revert (); // discard any uncommitted changes m_->revert (); // discard any uncommitted changes
} }
m_->setEditStrategy (QSqlTableModel::OnManualSubmit);
ConditionalTransaction transaction {*m_};
auto ok = m_->insertRecord (-1, record); auto ok = m_->insertRecord (-1, record);
if (ok) if (ok)
{ {
m_->select (); // to refresh views ok = transaction.submit (false);
} }
m_->setEditStrategy (QSqlTableModel::OnFieldChange);
return ok; return ok;
} }

View File

@ -84,13 +84,23 @@ AbstractLogWindow::AbstractLogWindow (QString const& settings_key, QSettings * s
: QWidget {parent} : QWidget {parent}
, m_ {this, settings_key, settings, configuration} , m_ {this, settings_key, settings, configuration}
{ {
// ensure view scrolls to latest new row // this attempt to scroll to the last new record doesn't work, some
connect (&m_->model_, &QAbstractItemModel::rowsInserted, this, [this] (QModelIndex const& parent, int /*first*/, int last) { // sort of issue with model indexes and optimized DB fetches. For
// note col 0 is hidden so use col 1 // now sorting by the same column and direction as the underlying DB
// queued connection required otherwise row may not be available // select and that DB select being in descending order so new rows
// in time // at the end appear at view row 0 gets the job done
if (m_->log_view_) m_->log_view_->scrollTo (m_->log_view_->model ()->index (last, 1, parent));
}, Qt::QueuedConnection); // // ensure view scrolls to latest new row
// connect (&m_->model_, &QAbstractItemModel::rowsInserted, this, [this] (QModelIndex const& parent, int first, int last) {
// // note col 0 is hidden so use col 1
// // queued connection required otherwise row may not be available
// // in time
// auto index = m_->model_.index (last, 1, parent);
// if (m_->log_view_)
// {
// m_->log_view_->scrollTo (index);
// }
// }, Qt::QueuedConnection);
} }
AbstractLogWindow::~AbstractLogWindow () AbstractLogWindow::~AbstractLogWindow ()
@ -105,24 +115,27 @@ void AbstractLogWindow::set_log_view (QTableView * log_view)
SettingsGroup g {m_->settings_, m_->settings_key_}; SettingsGroup g {m_->settings_, m_->settings_key_};
restoreGeometry (m_->settings_->value ("window/geometry").toByteArray ()); restoreGeometry (m_->settings_->value ("window/geometry").toByteArray ());
m_->log_view_ = log_view; m_->log_view_ = log_view;
m_->log_view_->setContextMenuPolicy (Qt::ActionsContextMenu); set_log_view_font (m_->configuration_->decoded_text_font ());
m_->log_view_->setAlternatingRowColors (true); log_view->setSortingEnabled (true);
m_->log_view_->setSelectionBehavior (QAbstractItemView::SelectRows); log_view->setContextMenuPolicy (Qt::ActionsContextMenu);
m_->log_view_->setSelectionMode (QAbstractItemView::ExtendedSelection); log_view->setAlternatingRowColors (true);
m_->log_view_->setVerticalScrollMode (QAbstractItemView::ScrollPerPixel); log_view->setSelectionBehavior (QAbstractItemView::SelectRows);
m_->model_.setSourceModel (m_->log_view_->model ()); log_view->setSelectionMode (QAbstractItemView::ExtendedSelection);
m_->log_view_->setModel (&m_->model_); log_view->setVerticalScrollMode (QAbstractItemView::ScrollPerPixel);
m_->log_view_->setColumnHidden (0, true); m_->model_.setSourceModel (log_view->model ());
log_view->setModel (&m_->model_);
log_view->setColumnHidden (0, true);
auto horizontal_header = log_view->horizontalHeader (); auto horizontal_header = log_view->horizontalHeader ();
horizontal_header->setResizeContentsPrecision (0); // visible region only
horizontal_header->setSectionResizeMode (QHeaderView::ResizeToContents); horizontal_header->setSectionResizeMode (QHeaderView::ResizeToContents);
horizontal_header->setSectionsMovable (true); horizontal_header->setSectionsMovable (true);
m_->log_view_->verticalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents); auto vertical_header = log_view->horizontalHeader ();
set_log_view_font (m_->configuration_->decoded_text_font ()); vertical_header->setResizeContentsPrecision (0); // visible region only
m_->log_view_->scrollToBottom (); vertical_header->setSectionResizeMode (QHeaderView::ResizeToContents);
// actions // actions
auto delete_action = new QAction {tr ("&Delete ..."), m_->log_view_}; auto delete_action = new QAction {tr ("&Delete ..."), log_view};
m_->log_view_->insertAction (nullptr, delete_action); log_view->insertAction (nullptr, delete_action);
connect (delete_action, &QAction::triggered, [this] (bool /*checked*/) { connect (delete_action, &QAction::triggered, [this] (bool /*checked*/) {
m_->delete_QSOs (); m_->delete_QSOs ();
}); });
@ -130,8 +143,8 @@ void AbstractLogWindow::set_log_view (QTableView * log_view)
void AbstractLogWindow::set_log_view_font (QFont const& font) void AbstractLogWindow::set_log_view_font (QFont const& font)
{ {
// m_->log_view_->setFont (font); m_->log_view_->setFont (font);
// m_->log_view_->horizontalHeader ()->setFont (font); m_->log_view_->horizontalHeader ()->setFont (font);
// m_->log_view_->verticalHeader ()->setFont (font); m_->log_view_->verticalHeader ()->setFont (font);
m_->model_.set_font (font); m_->model_.set_font (font);
} }

View File

@ -67,7 +67,8 @@ CabrilloLogWindow::CabrilloLogWindow (QSettings * settings, Configuration const
m_->ui_.log_table_view->setItemDelegateForColumn (2, new DateTimeAsSecsSinceEpochDelegate {this}); m_->ui_.log_table_view->setItemDelegateForColumn (2, new DateTimeAsSecsSinceEpochDelegate {this});
m_->ui_.log_table_view->setItemDelegateForColumn (3, new CallsignDelegate {this}); m_->ui_.log_table_view->setItemDelegateForColumn (3, new CallsignDelegate {this});
m_->ui_.log_table_view->setItemDelegateForColumn (6, new ForeignKeyDelegate {configuration->bands (), 0, this}); m_->ui_.log_table_view->setItemDelegateForColumn (6, new ForeignKeyDelegate {configuration->bands (), 0, this});
m_->ui_.log_table_view->horizontalHeader ()->moveSection (6, 1); // band to first column auto h_header = m_->ui_.log_table_view->horizontalHeader ();
h_header->moveSection (6, 1); // band to first column
} }
CabrilloLogWindow::~CabrilloLogWindow () CabrilloLogWindow::~CabrilloLogWindow ()

View File

@ -1,6 +1,8 @@
#include "echoplot.h" #include "echoplot.h"
#include "commons.h" #include "commons.h"
#include <math.h> #include <math.h>
#include <QPainter>
#include <QPen>
#include <QDebug> #include <QDebug>
#include "moc_echoplot.cpp" #include "moc_echoplot.cpp"

View File

@ -4,17 +4,20 @@
// For more details see the accompanying file LICENSE_WHEATLEY.TXT // For more details see the accompanying file LICENSE_WHEATLEY.TXT
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
#ifndef EPLOTTER_H #ifndef EPLOTTER_H_
#define EPLOTTER_H #define EPLOTTER_H_
#include <QtWidgets>
#include <QFrame> #include <QFrame>
#include <QSize>
#include <QImage> #include <QImage>
#include <cstring> #include <QString>
#define VERT_DIVS 7 //specify grid screen divisions #define VERT_DIVS 7 //specify grid screen divisions
#define HORZ_DIVS 20 #define HORZ_DIVS 20
class QPaintEvent;
class QResizeEvent;
class EPlotter : public QFrame class EPlotter : public QFrame
{ {
Q_OBJECT Q_OBJECT

View File

@ -3,6 +3,7 @@
#include "commons.h" #include "commons.h"
#include <QSettings> #include <QSettings>
#include <QApplication> #include <QApplication>
#include <QKeyEvent>
#include "fastplot.h" #include "fastplot.h"
#include "SettingsGroup.hpp" #include "SettingsGroup.hpp"

View File

@ -1,5 +1,5 @@
#ifndef FASTGRAPH_H #ifndef FASTGRAPH_H_
#define FASTGRAPH_H #define FASTGRAPH_H_
#include <QDialog> #include <QDialog>
#include <QScopedPointer> #include <QScopedPointer>
@ -9,6 +9,8 @@ namespace Ui {
} }
class QSettings; class QSettings;
class QCloseEvent;
class QKeyEvent;
class FastGraph : public QDialog class FastGraph : public QDialog
{ {

View File

@ -1,6 +1,10 @@
#include "fastplot.h" #include "fastplot.h"
#include "commons.h" #include "commons.h"
#include <math.h> #include <math.h>
#include <QPainter>
#include <QPen>
#include <QMouseEvent>
#include <QDateTime>
#include <QDebug> #include <QDebug>
#include "moc_fastplot.cpp" #include "moc_fastplot.cpp"

View File

@ -4,13 +4,16 @@
// For more details see the accompanying file LICENSE_WHEATLEY.TXT // For more details see the accompanying file LICENSE_WHEATLEY.TXT
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
#ifndef FPLOTTER_H #ifndef FPLOTTER_H_
#define FPLOTTER_H #define FPLOTTER_H_
#include <QtWidgets>
#include <QFrame> #include <QFrame>
#include <QImage> #include <QString>
#include <cstring> #include <QPixmap>
#include <QVector>
#include <QColor>
class QMouseEvent;
class FPlotter : public QFrame class FPlotter : public QFrame
{ {

View File

@ -1,6 +1,6 @@
// -*- Mode: C++ -*- // -*- Mode: C++ -*-
#ifndef LogQSO_H #ifndef LogQSO_H_
#define LogQSO_H #define LogQSO_H_
#include <QDialog> #include <QDialog>

View File

@ -1,12 +1,20 @@
//---------------------------------------------------------- MainWindow //---------------------------------------------------------- MainWindow
#include "mainwindow.h" #include "mainwindow.h"
#include <cinttypes> #include <cinttypes>
#include <cstring>
#include <limits> #include <limits>
#include <functional> #include <functional>
#include <fstream> #include <fstream>
#include <iterator> #include <iterator>
#include <algorithm> #include <algorithm>
#include <fftw3.h> #include <fftw3.h>
#include <QStringListModel>
#include <QSettings>
#include <QKeyEvent>
#include <QSharedMemory>
#include <QFileDialog>
#include <QTextBlock>
#include <QProgressBar>
#include <QLineEdit> #include <QLineEdit>
#include <QRegExpValidator> #include <QRegExpValidator>
#include <QRegExp> #include <QRegExp>
@ -3100,7 +3108,7 @@ void MainWindow::readFromStdout() //readFromStdout
if(w.at(0)==m_config.my_callsign() or w.at(0)==Radio::base_callsign(m_config.my_callsign())) { if(w.at(0)==m_config.my_callsign() or w.at(0)==Radio::base_callsign(m_config.my_callsign())) {
//### Check for ui->dxCallEntry->text()==foxCall before logging! ### //### Check for ui->dxCallEntry->text()==foxCall before logging! ###
ui->stopTxButton->click (); ui->stopTxButton->click ();
on_logQSOButton_clicked(); logQSOTimer.start(0);
} }
if((w.at(2)==m_config.my_callsign() or w.at(2)==Radio::base_callsign(m_config.my_callsign())) if((w.at(2)==m_config.my_callsign() or w.at(2)==Radio::base_callsign(m_config.my_callsign()))
and ui->tx3->text().length()>0) { and ui->tx3->text().length()>0) {
@ -3118,7 +3126,7 @@ void MainWindow::readFromStdout() //readFromStdout
ui->tx3->text().length()>0) { ui->tx3->text().length()>0) {
if(w.at(2)=="RR73") { if(w.at(2)=="RR73") {
ui->stopTxButton->click (); ui->stopTxButton->click ();
on_logQSOButton_clicked(); logQSOTimer.start(0);
} else { } else {
if(w.at(1)==Radio::base_callsign(ui->dxCallEntry->text()) and if(w.at(1)==Radio::base_callsign(ui->dxCallEntry->text()) and
(w.at(2).mid(0,1)=="+" or w.at(2).mid(0,1)=="-")) { (w.at(2).mid(0,1)=="+" or w.at(2).mid(0,1)=="-")) {
@ -4453,7 +4461,7 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie
m_nextCall=""; //### Temporary: disable use of "TU;" message m_nextCall=""; //### Temporary: disable use of "TU;" message
if(SpecOp::RTTY == m_config.special_op_id() and m_nextCall!="") { if(SpecOp::RTTY == m_config.special_op_id() and m_nextCall!="") {
// We're in RTTY contest and have "nextCall" queued up: send a "TU; ..." message // We're in RTTY contest and have "nextCall" queued up: send a "TU; ..." message
on_logQSOButton_clicked(); logQSOTimer.start(0);
ui->tx3->setText(ui->tx3->text().remove("TU; ")); ui->tx3->setText(ui->tx3->text().remove("TU; "));
useNextCall(); useNextCall();
QString t="TU; " + ui->tx3->text(); QString t="TU; " + ui->tx3->text();
@ -4462,7 +4470,7 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie
} else { } else {
// if(SpecOp::RTTY == m_config.special_op_id()) { // if(SpecOp::RTTY == m_config.special_op_id()) {
if(false) { if(false) {
on_logQSOButton_clicked(); logQSOTimer.start(0);
m_ntx=6; m_ntx=6;
ui->txrb6->setChecked(true); ui->txrb6->setChecked(true);
} else { } else {
@ -8216,7 +8224,7 @@ list2Done:
{ {
writeFoxQSO (QString {" Log: %1 %2 %3 %4 %5"}.arg (m_hisCall).arg (m_hisGrid) writeFoxQSO (QString {" Log: %1 %2 %3 %4 %5"}.arg (m_hisCall).arg (m_hisGrid)
.arg (m_rptSent).arg (m_rptRcvd).arg (m_lastBand)); .arg (m_rptSent).arg (m_rptRcvd).arg (m_lastBand));
on_logQSOButton_clicked(); logQSOTimer.start(0);
m_foxRateQueue.enqueue (now); //Add present time in seconds m_foxRateQueue.enqueue (now); //Add present time in seconds
//to Rate queue. //to Rate queue.
} }

View File

@ -1,12 +1,12 @@
// -*- Mode: C++ -*- // -*- Mode: C++ -*-
#ifndef MAINWINDOW_H #ifndef MAINWINDOW_H
#define MAINWINDOW_H #define MAINWINDOW_H
#ifdef QT5
#include <QtWidgets> #include <QMainWindow>
#else #include <QLabel>
#include <QtGui>
#endif
#include <QThread> #include <QThread>
#include <QProcess>
#include <QProgressBar>
#include <QTimer> #include <QTimer>
#include <QDateTime> #include <QDateTime>
#include <QList> #include <QList>
@ -58,6 +58,8 @@ namespace Ui {
class MainWindow; class MainWindow;
} }
class QSharedMemory;
class QSplashScreen;
class QSettings; class QSettings;
class QLineEdit; class QLineEdit;
class QFont; class QFont;
@ -102,6 +104,8 @@ public:
QWidget *parent = nullptr); QWidget *parent = nullptr);
~MainWindow(); ~MainWindow();
int decoderBusy () const {return m_decoderBusy;}
public slots: public slots:
void showSoundInError(const QString& errorMsg); void showSoundInError(const QString& errorMsg);
void showSoundOutError(const QString& errorMsg); void showSoundOutError(const QString& errorMsg);

View File

@ -1,5 +1,11 @@
#include "plotter.h" #include "plotter.h"
#include <math.h> #include <math.h>
#include <QAction>
#include <QMenu>
#include <QPainter>
#include <QDateTime>
#include <QPen>
#include <QMouseEvent>
#include <QDebug> #include <QDebug>
#include "commons.h" #include "commons.h"
#include "moc_plotter.cpp" #include "moc_plotter.cpp"

View File

@ -5,18 +5,14 @@
// For more details see the accompanying file LICENSE_WHEATLEY.TXT // For more details see the accompanying file LICENSE_WHEATLEY.TXT
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
#ifndef PLOTTER_H #ifndef PLOTTER_H_
#define PLOTTER_H #define PLOTTER_H_
#ifdef QT5
#include <QtWidgets>
#else
#include <QtGui>
#endif
#include <QFrame> #include <QFrame>
#include <QSize>
#include <QImage> #include <QImage>
#include <QVector> #include <QVector>
#include <cstring> #include <QColor>
#define VERT_DIVS 7 //specify grid screen divisions #define VERT_DIVS 7 //specify grid screen divisions
#define HORZ_DIVS 20 #define HORZ_DIVS 20

View File

@ -3,6 +3,8 @@
#include <algorithm> #include <algorithm>
#include <QApplication> #include <QApplication>
#include <QSettings> #include <QSettings>
#include <QDateTime>
#include <QKeyEvent>
#include "ui_widegraph.h" #include "ui_widegraph.h"
#include "commons.h" #include "commons.h"
#include "Configuration.hpp" #include "Configuration.hpp"

View File

@ -1,6 +1,6 @@
// -*- Mode: C++ -*- // -*- Mode: C++ -*-
#ifndef WIDEGRAPH_H #ifndef WIDEGRAPH_H_
#define WIDEGRAPH_H #define WIDEGRAPH_H_
#include <QDialog> #include <QDialog>
#include <QScopedPointer> #include <QScopedPointer>

View File

@ -19,7 +19,7 @@ HEADERS += \
widgets/echoplot.h widgets/echograph.h widgets/fastgraph.h \ widgets/echoplot.h widgets/echograph.h widgets/fastgraph.h \
widgets/fastplot.h widgets/MessageBox.hpp widgets/colorhighlighting.h \ widgets/fastplot.h widgets/MessageBox.hpp widgets/colorhighlighting.h \
widgets/ExportCabrillo.h widgets/AbstractLogWindow.hpp \ widgets/ExportCabrillo.h widgets/AbstractLogWindow.hpp \
widgets/FoxLogWindow.cpp widgets/CabrilloLogWindow.cpp widgets/FoxLogWindow.hpp widgets/CabrilloLogWindow.hpp
FORMS += \ FORMS += \
widgets/mainwindow.ui widgets/about.ui \ widgets/mainwindow.ui widgets/about.ui \