Imported existing sources
This commit is contained in:
30
common.h
Normal file
30
common.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QHash>
|
||||
#include <QDate>
|
||||
#include <QtGlobal>
|
||||
#include <QMetaType>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using Host = QString;
|
||||
using Process = QString;
|
||||
using Filename = QString;
|
||||
struct Logfile {
|
||||
QString filename;
|
||||
QString filepath;
|
||||
qint64 filesize;
|
||||
bool gzipCompressed;
|
||||
};
|
||||
using ScanResult = QHash<Host, QHash<Process, QHash<QDate, Logfile> > >;
|
||||
Q_DECLARE_METATYPE(ScanResult)
|
||||
|
||||
inline bool scanResultEmpty(const ScanResult &result)
|
||||
{
|
||||
return result.isEmpty() || std::all_of(result.constBegin(), result.constEnd(), [](const auto &host){
|
||||
return host.isEmpty() || std::all_of(host.constBegin(), host.constEnd(), [](const auto &process){
|
||||
return process.isEmpty();
|
||||
});
|
||||
});
|
||||
}
|
104
dialogs/graphdialog.cpp
Normal file
104
dialogs/graphdialog.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#include "graphdialog.h"
|
||||
#include "ui_graphdialog.h"
|
||||
|
||||
#include <QSqlQuery>
|
||||
#include <QSqlError>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
|
||||
#include <QLineSeries>
|
||||
#include <QDateTimeAxis>
|
||||
#include <QValueAxis>
|
||||
|
||||
GraphDialog::GraphDialog(QSqlDatabase &database, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
m_ui(std::make_unique<Ui::GraphDialog>()),
|
||||
m_database(database)
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
QString sql;
|
||||
|
||||
if (m_database.driverName() == "QSQLITE")
|
||||
{
|
||||
sql = "SELECT "
|
||||
"`Timestamp`, "
|
||||
"COUNT(`Timestamp`) "
|
||||
"FROM "
|
||||
"`Logs` "
|
||||
"GROUP BY "
|
||||
"STRFTIME('%Y-%m-%d %H:00:00.000', `Timestamp`)";
|
||||
}
|
||||
else if (m_database.driverName() == "QMYSQL")
|
||||
{
|
||||
sql = "SELECT "
|
||||
"`Timestamp`, "
|
||||
"COUNT(`Timestamp`) "
|
||||
"FROM "
|
||||
"`Logs` "
|
||||
"GROUP BY "
|
||||
"DATE_FORMAT(`Timestamp`, '%Y-%m-%d %H:%i:00.000')";
|
||||
}
|
||||
else
|
||||
qFatal("unknown sql driver");
|
||||
|
||||
QSqlQuery query(sql, m_database);
|
||||
if (query.lastError().isValid())
|
||||
qCritical() << query.lastError().text();
|
||||
|
||||
auto chart = new QtCharts::QChart();
|
||||
chart->legend()->hide();
|
||||
chart->setTitle(tr("Charts-Test"));
|
||||
|
||||
auto series = new QtCharts::QLineSeries();
|
||||
chart->addSeries(series);
|
||||
|
||||
QDateTime minDt, maxDt;
|
||||
int maxCount{};
|
||||
|
||||
while (query.next())
|
||||
{
|
||||
const auto timestampStr = query.value(0).toString();
|
||||
qDebug() << timestampStr;
|
||||
|
||||
const auto timestamp = QDateTime::fromString(timestampStr, QStringLiteral("yyyy-MM-dd HH:mm:ss.zzz"));
|
||||
Q_ASSERT(timestamp.isValid());
|
||||
|
||||
if (minDt.isNull() || timestamp < minDt)
|
||||
minDt = timestamp;
|
||||
|
||||
if (maxDt.isNull() || timestamp > maxDt)
|
||||
maxDt = timestamp;
|
||||
|
||||
const auto count = query.value(1).toInt();
|
||||
|
||||
if (count > maxCount)
|
||||
maxCount = count;
|
||||
|
||||
qDebug() << timestamp << count;
|
||||
|
||||
series->append(timestamp.toMSecsSinceEpoch(), count);
|
||||
}
|
||||
|
||||
qDebug() << minDt << maxDt;
|
||||
|
||||
auto axisX = new QtCharts::QDateTimeAxis;
|
||||
axisX->setRange(minDt, maxDt);
|
||||
axisX->setTickCount(20);
|
||||
axisX->setFormat("HH:mm:ss");
|
||||
axisX->setTitleText("Timestamp");
|
||||
chart->addAxis(axisX, Qt::AlignBottom);
|
||||
series->attachAxis(axisX);
|
||||
|
||||
auto axisY = new QtCharts::QValueAxis;
|
||||
axisY->setMax(maxCount);
|
||||
axisY->setLabelFormat("%i");
|
||||
axisY->setTitleText("Logs count");
|
||||
chart->addAxis(axisY, Qt::AlignLeft);
|
||||
series->attachAxis(axisY);
|
||||
|
||||
m_ui->chartView->setRenderHint(QPainter::Antialiasing);
|
||||
m_ui->chartView->setChart(chart);
|
||||
}
|
||||
|
||||
GraphDialog::~GraphDialog() = default;
|
23
dialogs/graphdialog.h
Normal file
23
dialogs/graphdialog.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class QSqlDatabase;
|
||||
|
||||
namespace Ui { class GraphDialog; }
|
||||
|
||||
class GraphDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GraphDialog(QSqlDatabase &database, QWidget *parent = nullptr);
|
||||
~GraphDialog() override;
|
||||
|
||||
private:
|
||||
const std::unique_ptr<Ui::GraphDialog> m_ui;
|
||||
|
||||
QSqlDatabase &m_database;
|
||||
};
|
55
dialogs/graphdialog.ui
Normal file
55
dialogs/graphdialog.ui
Normal file
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>GraphDialog</class>
|
||||
<widget class="QDialog" name="GraphDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QtCharts::QChartView" name="chartView"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QtCharts::QChartView</class>
|
||||
<extends>QGraphicsView</extends>
|
||||
<header>qchartview.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>GraphDialog</receiver>
|
||||
<slot>close()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>199</x>
|
||||
<y>278</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>199</x>
|
||||
<y>149</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
36
dialogs/opendialog.cpp
Normal file
36
dialogs/opendialog.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "opendialog.h"
|
||||
#include "ui_opendialog.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QStringBuilder>
|
||||
#include <QSqlError>
|
||||
|
||||
OpenDialog::OpenDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
m_ui(std::make_unique<Ui::OpenDialog>())
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &OpenDialog::submit);
|
||||
}
|
||||
|
||||
OpenDialog::~OpenDialog() = default;
|
||||
|
||||
QSqlDatabase OpenDialog::database()
|
||||
{
|
||||
return m_database;
|
||||
}
|
||||
|
||||
void OpenDialog::submit()
|
||||
{
|
||||
m_database = m_ui->databaseWidget->createConnection();
|
||||
|
||||
if (!m_database.open())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Could not open database!"), tr("Could not open database!") % "\n\n" % m_database.lastError().text());
|
||||
m_database = {};
|
||||
return;
|
||||
}
|
||||
|
||||
accept();
|
||||
}
|
27
dialogs/opendialog.h
Normal file
27
dialogs/opendialog.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QSqlDatabase>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Ui { class OpenDialog; }
|
||||
|
||||
class OpenDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit OpenDialog(QWidget *parent = nullptr);
|
||||
~OpenDialog() override;
|
||||
|
||||
QSqlDatabase database();
|
||||
|
||||
private slots:
|
||||
void submit();
|
||||
|
||||
private:
|
||||
const std::unique_ptr<Ui::OpenDialog> m_ui;
|
||||
|
||||
QSqlDatabase m_database;
|
||||
};
|
59
dialogs/opendialog.ui
Normal file
59
dialogs/opendialog.ui
Normal file
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OpenDialog</class>
|
||||
<widget class="QDialog" name="OpenDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,0">
|
||||
<item>
|
||||
<widget class="DatabaseWidget" name="databaseWidget" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>DatabaseWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>widgets/databasewidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>OpenDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
BIN
failed.png
Normal file
BIN
failed.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 991 B |
77
gzipdevice.cpp
Normal file
77
gzipdevice.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include "gzipdevice.h"
|
||||
|
||||
#include <QFile>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
GzipDevice::GzipDevice(QFile &file, QObject *parent) :
|
||||
QIODevice(parent),
|
||||
m_file(file)
|
||||
{
|
||||
if (!m_file.isOpen())
|
||||
throw std::runtime_error("file is not open");
|
||||
|
||||
setOpenMode(QIODevice::ReadOnly);
|
||||
|
||||
// Prepare inflater status
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
strm.avail_in = 0;
|
||||
strm.next_in = Z_NULL;
|
||||
|
||||
// Initialize inflater
|
||||
m_result = inflateInit2(&strm, 15 + 16);
|
||||
if (m_result != Z_OK)
|
||||
throw std::runtime_error("could not init z_stream");
|
||||
}
|
||||
|
||||
GzipDevice::~GzipDevice()
|
||||
{
|
||||
inflateEnd(&strm);
|
||||
}
|
||||
|
||||
bool GzipDevice::isSequential() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GzipDevice::atEnd() const
|
||||
{
|
||||
return m_result == Z_STREAM_END;
|
||||
}
|
||||
|
||||
qint64 GzipDevice::readData(char *data, qint64 maxlen)
|
||||
{
|
||||
if (strm.avail_in == 0)
|
||||
{
|
||||
strm.next_in = reinterpret_cast<unsigned char *>(m_readBuffer);
|
||||
strm.avail_in = m_file.read(m_readBuffer, m_readBufferSize);
|
||||
}
|
||||
|
||||
strm.next_out = reinterpret_cast<unsigned char*>(data);
|
||||
strm.avail_out = maxlen;
|
||||
|
||||
m_result = inflate(&strm, Z_NO_FLUSH);
|
||||
|
||||
switch (m_result) {
|
||||
case Z_NEED_DICT:
|
||||
throw std::runtime_error("decompression failed: Z_NEED_DICT");
|
||||
case Z_DATA_ERROR:
|
||||
throw std::runtime_error("decompression failed: Z_DATA_ERROR");
|
||||
case Z_MEM_ERROR:
|
||||
throw std::runtime_error("decompression failed: Z_MEM_ERROR");
|
||||
case Z_STREAM_ERROR:
|
||||
throw std::runtime_error("decompression failed: Z_STREAM_ERROR");
|
||||
}
|
||||
|
||||
return maxlen-strm.avail_out;
|
||||
}
|
||||
|
||||
qint64 GzipDevice::writeData(const char *data, qint64 len)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(len)
|
||||
qFatal("no writes allowed in GzipDevice!");
|
||||
return -1;
|
||||
}
|
28
gzipdevice.h
Normal file
28
gzipdevice.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <QIODevice>
|
||||
|
||||
#include "zlib.h"
|
||||
|
||||
class QFile;
|
||||
|
||||
class GzipDevice : public QIODevice
|
||||
{
|
||||
public:
|
||||
GzipDevice(QFile &file, QObject *parent = nullptr);
|
||||
~GzipDevice() override;
|
||||
|
||||
bool isSequential() const override;
|
||||
bool atEnd() const override;
|
||||
|
||||
protected:
|
||||
qint64 readData(char *data, qint64 maxlen) override;
|
||||
qint64 writeData(const char *data, qint64 len) override;
|
||||
|
||||
private:
|
||||
QFile &m_file;
|
||||
static constexpr std::size_t m_readBufferSize { 32 * 1024 };
|
||||
char m_readBuffer[m_readBufferSize];
|
||||
z_stream strm;
|
||||
int m_result;
|
||||
};
|
BIN
loading.gif
Normal file
BIN
loading.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
76
loganalyzer.pro
Normal file
76
loganalyzer.pro
Normal file
@@ -0,0 +1,76 @@
|
||||
QT += core gui widgets network sql charts
|
||||
|
||||
TARGET = loganalyzer
|
||||
TEMPLATE = app
|
||||
|
||||
DEFINES += QT_DEPRECATED_WARNINGS QT_DISABLE_DEPRECATED_BEFORE=0x060000
|
||||
|
||||
CONFIG += c++14
|
||||
QMAKE_CXXFLAGS_RELEASE -= -O1
|
||||
QMAKE_CXXFLAGS_RELEASE -= -O2
|
||||
QMAKE_CXXFLAGS_RELEASE *= -O3
|
||||
QMAKE_CXXFLAGS_RELEASE -= -march=x86-64
|
||||
QMAKE_CXXFLAGS_RELEASE -= -mtune=generic
|
||||
QMAKE_CXXFLAGS_RELEASE *= -march=native
|
||||
QMAKE_CXXFLAGS_RELEASE *= -mtune=native
|
||||
|
||||
LIBS += -lz
|
||||
|
||||
SOURCES += main.cpp \
|
||||
mainwindow.cpp \
|
||||
wizard/importwizard.cpp \
|
||||
wizard/intropage.cpp \
|
||||
wizard/databasepage.cpp \
|
||||
wizard/importtypepage.cpp \
|
||||
wizard/localimportpage.cpp \
|
||||
wizard/conclusionpage.cpp \
|
||||
wizard/remoteimportoverviewpage.cpp \
|
||||
wizard/tablespage.cpp \
|
||||
threads/tablecreatorthread.cpp \
|
||||
models/checklistmodel.cpp \
|
||||
threads/remotescannerthread.cpp \
|
||||
wizard/remoteimportscanpage.cpp \
|
||||
threads/importthread.cpp \
|
||||
wizard/importprogresspage.cpp \
|
||||
dialogs/opendialog.cpp \
|
||||
widgets/fileselectionwidget.cpp \
|
||||
widgets/databasewidget.cpp \
|
||||
gzipdevice.cpp \
|
||||
dialogs/graphdialog.cpp \
|
||||
models/sqlrelationaltablemodel.cpp
|
||||
|
||||
HEADERS += \
|
||||
mainwindow.h \
|
||||
wizard/importwizard.h \
|
||||
wizard/intropage.h \
|
||||
wizard/databasepage.h \
|
||||
wizard/importtypepage.h \
|
||||
wizard/localimportpage.h \
|
||||
wizard/conclusionpage.h \
|
||||
wizard/remoteimportoverviewpage.h \
|
||||
wizard/tablespage.h \
|
||||
threads/tablecreatorthread.h \
|
||||
models/checklistmodel.h \
|
||||
threads/remotescannerthread.h \
|
||||
wizard/remoteimportscanpage.h \
|
||||
common.h \
|
||||
threads/importthread.h \
|
||||
wizard/importprogresspage.h \
|
||||
dialogs/opendialog.h \
|
||||
widgets/fileselectionwidget.h \
|
||||
widgets/databasewidget.h \
|
||||
gzipdevice.h \
|
||||
dialogs/graphdialog.h \
|
||||
models/sqlrelationaltablemodel.h
|
||||
|
||||
FORMS += \
|
||||
mainwindow.ui \
|
||||
dialogs/opendialog.ui \
|
||||
widgets/fileselectionwidget.ui \
|
||||
widgets/databasewidget.ui \
|
||||
wizard/intropage.ui \
|
||||
wizard/databasepage.ui \
|
||||
dialogs/graphdialog.ui
|
||||
|
||||
RESOURCES += \
|
||||
resources.qrc
|
17
main.cpp
Normal file
17
main.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include <QApplication>
|
||||
#include <QMetaType>
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include "common.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
|
||||
qRegisterMetaType<ScanResult>();
|
||||
|
||||
MainWindow w;
|
||||
w.show();
|
||||
|
||||
return a.exec();
|
||||
}
|
217
mainwindow.cpp
Normal file
217
mainwindow.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
#include <QSqlRelationalDelegate>
|
||||
#include <QSqlQuery>
|
||||
#include <QSqlError>
|
||||
#include <QMessageBox>
|
||||
#include <QStringBuilder>
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include <QDateTime>
|
||||
#include <QSqlRecord>
|
||||
#include <QSqlRelation>
|
||||
#include <QDebug>
|
||||
|
||||
#include "wizard/importwizard.h"
|
||||
#include "dialogs/opendialog.h"
|
||||
#include "dialogs/graphdialog.h"
|
||||
#include "models/sqlrelationaltablemodel.h"
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
m_ui(std::make_unique<Ui::MainWindow>())
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
m_ui->actionNew->setShortcut(QKeySequence::New);
|
||||
m_ui->actionOpen->setShortcut(QKeySequence::Open);
|
||||
m_ui->actionQuit->setShortcut(QKeySequence::Quit);
|
||||
|
||||
connect(m_ui->actionNew, &QAction::triggered, this, &MainWindow::newClicked);
|
||||
connect(m_ui->actionOpen, &QAction::triggered, this, &MainWindow::openClicked);
|
||||
connect(m_ui->actionClose, &QAction::triggered, this, &MainWindow::closeClicked);
|
||||
connect(m_ui->actionQuit, &QAction::triggered, QCoreApplication::instance(), &QCoreApplication::quit);
|
||||
connect(m_ui->actionGraph, &QAction::triggered, this, &MainWindow::graphClicked);
|
||||
|
||||
for (QAction *action : { m_ui->actionTimestamp, m_ui->actionHost, m_ui->actionProcess, m_ui->actionFilename, m_ui->actionThread, m_ui->actionType, m_ui->actionMessage })
|
||||
connect(action, &QAction::toggled, this, &MainWindow::showColumns);
|
||||
|
||||
connect(m_ui->lineEdit, &QLineEdit::returnPressed, this, &MainWindow::updateQuery);
|
||||
connect(m_ui->pushButton, &QPushButton::pressed, this, &MainWindow::updateQuery);
|
||||
|
||||
m_ui->tableView->setItemDelegate(new QSqlRelationalDelegate(m_ui->tableView));
|
||||
|
||||
connect(m_ui->tableView, &QWidget::customContextMenuRequested, this, &MainWindow::showContextMenu);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow() = default;
|
||||
|
||||
void MainWindow::newClicked()
|
||||
{
|
||||
ImportWizard wizard(this);
|
||||
if (wizard.exec() == QDialog::Accepted && wizard.field("open").toBool())
|
||||
{
|
||||
m_ui->actionNew->setVisible(false);
|
||||
m_ui->actionOpen->setVisible(false);
|
||||
m_ui->actionClose->setVisible(true);
|
||||
m_ui->actionGraph->setEnabled(true);
|
||||
m_ui->lineEdit->setEnabled(true);
|
||||
m_ui->pushButton->setEnabled(true);
|
||||
|
||||
m_database = wizard.database();
|
||||
setupModel();
|
||||
updateQuery();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::openClicked()
|
||||
{
|
||||
OpenDialog dialog(this);
|
||||
if (dialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
m_ui->actionNew->setVisible(false);
|
||||
m_ui->actionOpen->setVisible(false);
|
||||
m_ui->actionClose->setVisible(true);
|
||||
m_ui->actionGraph->setEnabled(true);
|
||||
m_ui->lineEdit->setEnabled(true);
|
||||
m_ui->pushButton->setEnabled(true);
|
||||
|
||||
m_database = dialog.database();
|
||||
setupModel();
|
||||
updateQuery();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::closeClicked()
|
||||
{
|
||||
m_ui->actionNew->setVisible(true);
|
||||
m_ui->actionOpen->setVisible(true);
|
||||
m_ui->actionClose->setVisible(false);
|
||||
m_ui->actionGraph->setEnabled(false);
|
||||
m_ui->lineEdit->setEnabled(false);
|
||||
m_ui->pushButton->setEnabled(false);
|
||||
|
||||
m_ui->tableView->setModel(nullptr);
|
||||
m_model = nullptr;
|
||||
m_database.close();
|
||||
m_database = QSqlDatabase();
|
||||
}
|
||||
|
||||
void MainWindow::graphClicked()
|
||||
{
|
||||
GraphDialog(m_database, this).exec();
|
||||
}
|
||||
|
||||
void MainWindow::updateQuery()
|
||||
{
|
||||
auto filter = m_ui->lineEdit->text();
|
||||
if (!filter.trimmed().isEmpty())
|
||||
{
|
||||
filter.replace("||", "OR");
|
||||
filter.replace("&&", "AND");
|
||||
m_model->setFilter(filter);
|
||||
}
|
||||
|
||||
if (!m_model->select())
|
||||
QMessageBox::warning(this, tr("Query failed!"), tr("Query failed!") % "\n\n" % m_model->query().lastError().text());
|
||||
}
|
||||
|
||||
void MainWindow::showColumns()
|
||||
{
|
||||
for (const auto &pair : {
|
||||
std::make_pair(m_ui->actionTimestamp, ColumnTimestamp),
|
||||
std::make_pair(m_ui->actionHost, ColumnHost),
|
||||
std::make_pair(m_ui->actionProcess, ColumnProcess),
|
||||
std::make_pair(m_ui->actionFilename, ColumnFilename),
|
||||
std::make_pair(m_ui->actionThread, ColumnThread),
|
||||
std::make_pair(m_ui->actionType, ColumnType),
|
||||
std::make_pair(m_ui->actionMessage, ColumnMessage)
|
||||
})
|
||||
m_ui->tableView->setColumnHidden(std::get<1>(pair), !std::get<0>(pair)->isChecked());
|
||||
}
|
||||
|
||||
void MainWindow::showContextMenu(const QPoint &pos)
|
||||
{
|
||||
const auto index = m_ui->tableView->indexAt(pos);
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
QMenu menu(this);
|
||||
const auto exec = [this,&menu,&pos](){ return menu.exec(m_ui->tableView->viewport()->mapToGlobal(pos)); };
|
||||
|
||||
qDebug() << m_model->record(index.row()).value(2);
|
||||
const auto data = m_model->data(index, Qt::EditRole).toString();
|
||||
|
||||
switch (index.column())
|
||||
{
|
||||
case ColumnTimestamp:
|
||||
{
|
||||
auto minute = menu.addAction(tr("Filter by minute"));
|
||||
auto second = menu.addAction(tr("Filter by second"));
|
||||
auto action = exec();
|
||||
if (action == minute || action == second)
|
||||
{
|
||||
const auto format = QStringLiteral("yyyy-MM-dd HH:mm:ss.zzz");
|
||||
|
||||
auto dateTime = QDateTime::fromString(data, format);
|
||||
auto time = dateTime.time();
|
||||
time.setHMS(time.hour(), time.minute(), action==minute ? 0 : time.second());
|
||||
dateTime.setTime(time);
|
||||
m_ui->lineEdit->setText(QString("`Timestamp` BETWEEN \"%0\" AND \"%1\"").arg(dateTime.toString(format), dateTime.addMSecs(action==minute ? 59999 : 999).toString(format)));
|
||||
updateQuery();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ColumnHost:
|
||||
if (menu.addAction(tr("Filter by host")) == exec())
|
||||
{
|
||||
m_ui->lineEdit->setText(QString("`Hosts`.`Name` = \"%0\"").arg(data));
|
||||
updateQuery();
|
||||
}
|
||||
break;
|
||||
case ColumnProcess:
|
||||
if (menu.addAction(tr("Filter by process")) == exec())
|
||||
{
|
||||
m_ui->lineEdit->setText(QString("`Processes`.`Name` = \"%0\"").arg(data));
|
||||
updateQuery();
|
||||
}
|
||||
break;
|
||||
case ColumnFilename:
|
||||
if (menu.addAction(tr("Filter by filename")) == exec())
|
||||
{
|
||||
m_ui->lineEdit->setText(QString("`Filenames`.`Name` = \"%0\"").arg(data));
|
||||
updateQuery();
|
||||
}
|
||||
break;
|
||||
case ColumnThread:
|
||||
if (menu.addAction(tr("Filter by thread")) == exec())
|
||||
{
|
||||
m_ui->lineEdit->setText(QString("`Threads`.`Name` = \"%0\"").arg(data));
|
||||
updateQuery();
|
||||
}
|
||||
break;
|
||||
case ColumnType:
|
||||
if (menu.addAction(tr("Filter by type")) == exec())
|
||||
{
|
||||
m_ui->lineEdit->setText(QString("`Types`.`Name` = \"%0\"").arg(data));
|
||||
updateQuery();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::setupModel()
|
||||
{
|
||||
m_ui->tableView->setModel(nullptr);
|
||||
m_model = std::make_unique<SqlRelationalTableModel>(this, m_database);
|
||||
m_model->setTable("Logs");
|
||||
m_model->setRelation(ColumnHost, QSqlRelation("Hosts", "ID", "Name"));
|
||||
m_model->setRelation(ColumnProcess, QSqlRelation("Processes", "ID", "Name"));
|
||||
m_model->setRelation(ColumnFilename, QSqlRelation("Filenames", "ID", "Name"));
|
||||
m_model->setRelation(ColumnThread, QSqlRelation("Threads", "ID", "Name"));
|
||||
m_model->setRelation(ColumnType, QSqlRelation("Types", "ID", "Name"));
|
||||
m_ui->tableView->setModel(m_model.get());
|
||||
m_ui->tableView->setColumnHidden(ColumnID, true);
|
||||
showColumns();
|
||||
}
|
38
mainwindow.h
Normal file
38
mainwindow.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QSqlDatabase>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class QSqlRelationalTableModel;
|
||||
|
||||
namespace Ui { class MainWindow; }
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow() override;
|
||||
|
||||
private slots:
|
||||
void newClicked();
|
||||
void openClicked();
|
||||
void closeClicked();
|
||||
void graphClicked();
|
||||
void updateQuery();
|
||||
void showColumns();
|
||||
void showContextMenu(const QPoint &pos);
|
||||
|
||||
private:
|
||||
void setupModel();
|
||||
|
||||
enum { ColumnID, ColumnTimestamp, ColumnHost, ColumnProcess, ColumnFilename, ColumnThread, ColumnType, ColumnMessage };
|
||||
|
||||
const std::unique_ptr<Ui::MainWindow> m_ui;
|
||||
|
||||
QSqlDatabase m_database;
|
||||
std::unique_ptr<QSqlRelationalTableModel> m_model;
|
||||
};
|
216
mainwindow.ui
Normal file
216
mainwindow.ui
Normal file
@@ -0,0 +1,216 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>965</width>
|
||||
<height>578</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralWidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEdit">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Filter</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableView" name="tableView">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menuBar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>965</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menu_File">
|
||||
<property name="title">
|
||||
<string>&File</string>
|
||||
</property>
|
||||
<addaction name="actionNew"/>
|
||||
<addaction name="actionOpen"/>
|
||||
<addaction name="actionClose"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionQuit"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuView">
|
||||
<property name="title">
|
||||
<string>&View</string>
|
||||
</property>
|
||||
<addaction name="actionTimestamp"/>
|
||||
<addaction name="actionHost"/>
|
||||
<addaction name="actionProcess"/>
|
||||
<addaction name="actionFilename"/>
|
||||
<addaction name="actionThread"/>
|
||||
<addaction name="actionType"/>
|
||||
<addaction name="actionMessage"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuTools">
|
||||
<property name="title">
|
||||
<string>&Tools</string>
|
||||
</property>
|
||||
<addaction name="actionGraph"/>
|
||||
</widget>
|
||||
<addaction name="menu_File"/>
|
||||
<addaction name="menuView"/>
|
||||
<addaction name="menuTools"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusBar"/>
|
||||
<action name="actionNew">
|
||||
<property name="text">
|
||||
<string>&New project</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionOpen">
|
||||
<property name="text">
|
||||
<string>&Open</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionQuit">
|
||||
<property name="text">
|
||||
<string>Quit</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionClose">
|
||||
<property name="text">
|
||||
<string>Close Database</string>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionTimestamp">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Timestamp</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionHost">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Host</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionProcess">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Process</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFilename">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Filename</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionThread">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Thread</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionType">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionMessage">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Message</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGraph">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Graph</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRawData">
|
||||
<property name="text">
|
||||
<string>Raw data</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
141
models/checklistmodel.cpp
Normal file
141
models/checklistmodel.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
#include "checklistmodel.h"
|
||||
|
||||
ChecklistModel::ChecklistModel(QObject *parent) :
|
||||
QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
ChecklistModel::ChecklistModel(const QStringList &items, QObject *parent) :
|
||||
QAbstractListModel(parent)
|
||||
{
|
||||
for (const auto &item : items)
|
||||
m_items.append(std::make_pair(item, true));
|
||||
}
|
||||
|
||||
ChecklistModel::ChecklistModel(const QList<std::pair<QString, bool> > &items, QObject *parent) :
|
||||
QAbstractListModel(parent),
|
||||
m_items(items)
|
||||
{
|
||||
}
|
||||
|
||||
QStringList ChecklistModel::items() const
|
||||
{
|
||||
QStringList items;
|
||||
|
||||
for (const auto &pair : m_items)
|
||||
items.append(std::get<0>(pair));
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
void ChecklistModel::setItems(const QStringList &items)
|
||||
{
|
||||
emit beginResetModel();
|
||||
|
||||
m_items.clear();
|
||||
|
||||
for (const auto &item : items)
|
||||
m_items.append(std::make_pair(item, true));
|
||||
|
||||
emit endResetModel();
|
||||
}
|
||||
|
||||
void ChecklistModel::setItems(const QList<std::pair<QString, bool> > &items)
|
||||
{
|
||||
emit beginResetModel();
|
||||
|
||||
m_items = items;
|
||||
|
||||
emit endResetModel();
|
||||
}
|
||||
|
||||
QStringList ChecklistModel::enabledItems() const
|
||||
{
|
||||
QStringList items;
|
||||
|
||||
for (const auto &pair : m_items)
|
||||
if (std::get<1>(pair))
|
||||
items.append(std::get<0>(pair));
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
QStringList ChecklistModel::disabledItems() const
|
||||
{
|
||||
QStringList items;
|
||||
|
||||
for (const auto &pair : m_items)
|
||||
if (!std::get<1>(pair))
|
||||
items.append(std::get<0>(pair));
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
int ChecklistModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.isValid())
|
||||
return 0;
|
||||
return m_items.count();
|
||||
}
|
||||
|
||||
QModelIndex ChecklistModel::sibling(int row, int column, const QModelIndex &idx) const
|
||||
{
|
||||
if (!idx.isValid() || column != 0 || row >= m_items.count() || row < 0)
|
||||
return QModelIndex();
|
||||
return createIndex(row, 0);
|
||||
}
|
||||
|
||||
QMap<int, QVariant> ChecklistModel::itemData(const QModelIndex &index) const
|
||||
{
|
||||
if (!checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::ParentIsInvalid))
|
||||
return QMap<int, QVariant>{};
|
||||
const auto &item = m_items.at(index.row());
|
||||
return QMap<int, QVariant>{{
|
||||
std::make_pair<int>(Qt::DisplayRole, std::get<0>(item)),
|
||||
std::make_pair<int>(Qt::EditRole, std::get<0>(item)),
|
||||
std::make_pair<int>(Qt::CheckStateRole, std::get<1>(item) ? Qt::Checked : Qt::Unchecked)
|
||||
}};
|
||||
}
|
||||
|
||||
bool ChecklistModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles)
|
||||
{
|
||||
if (roles.isEmpty())
|
||||
return false;
|
||||
if (std::any_of(roles.keyBegin(), roles.keyEnd(), [](int role) { return role != Qt::CheckStateRole; }))
|
||||
return false;
|
||||
auto roleIter = roles.constFind(Qt::CheckStateRole);
|
||||
Q_ASSERT(roleIter != roles.constEnd());
|
||||
return setData(index, roleIter.value(), roleIter.key());
|
||||
}
|
||||
|
||||
QVariant ChecklistModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (index.row() < 0 || index.row() >= m_items.size())
|
||||
return QVariant();
|
||||
const auto &item = m_items.at(index.row());
|
||||
if (role == Qt::DisplayRole || role == Qt::EditRole)
|
||||
return std::get<0>(item);
|
||||
if (role == Qt::CheckStateRole)
|
||||
return std::get<1>(item) ? Qt::Checked : Qt::Unchecked;
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool ChecklistModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
if (index.row() >= 0 && index.row() < m_items.size() && role == Qt::CheckStateRole)
|
||||
{
|
||||
auto &item = m_items[index.row()];
|
||||
std::get<1>(item) = value.toBool();
|
||||
emit dataChanged(index, index, { Qt::CheckStateRole });
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Qt::ItemFlags ChecklistModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QAbstractListModel::flags(index);
|
||||
return QAbstractListModel::flags(index) | Qt::ItemIsUserCheckable;
|
||||
}
|
33
models/checklistmodel.h
Normal file
33
models/checklistmodel.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QList>
|
||||
|
||||
#include <utility>
|
||||
|
||||
class Q_CORE_EXPORT ChecklistModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ChecklistModel(QObject *parent = nullptr);
|
||||
explicit ChecklistModel(const QStringList &items, QObject *parent = nullptr);
|
||||
explicit ChecklistModel(const QList<std::pair<QString, bool> > &strings, QObject *parent = nullptr);
|
||||
|
||||
QStringList items() const;
|
||||
void setItems(const QStringList &items);
|
||||
void setItems(const QList<std::pair<QString, bool> > &items);
|
||||
QStringList enabledItems() const;
|
||||
QStringList disabledItems() const;
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QModelIndex sibling(int row, int column, const QModelIndex &idx) const override;
|
||||
QMap<int, QVariant> itemData(const QModelIndex &index) const override;
|
||||
bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles) override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
|
||||
private:
|
||||
QList<std::pair<QString, bool> > m_items;
|
||||
};
|
14
models/sqlrelationaltablemodel.cpp
Normal file
14
models/sqlrelationaltablemodel.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "sqlrelationaltablemodel.h"
|
||||
|
||||
SqlRelationalTableModel::SqlRelationalTableModel(QObject *parent, QSqlDatabase db) :
|
||||
QSqlRelationalTableModel(parent, db)
|
||||
{
|
||||
}
|
||||
|
||||
QVariant SqlRelationalTableModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (orientation == Qt::Vertical)
|
||||
return {};
|
||||
|
||||
return QSqlRelationalTableModel::headerData(section, orientation, role);
|
||||
}
|
13
models/sqlrelationaltablemodel.h
Normal file
13
models/sqlrelationaltablemodel.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <QSqlRelationalTableModel>
|
||||
|
||||
class SqlRelationalTableModel : public QSqlRelationalTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SqlRelationalTableModel(QObject *parent = nullptr, QSqlDatabase db = QSqlDatabase());
|
||||
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
};
|
7
resources.qrc
Normal file
7
resources.qrc
Normal file
@@ -0,0 +1,7 @@
|
||||
<RCC>
|
||||
<qresource prefix="/loganalyzer">
|
||||
<file>loading.gif</file>
|
||||
<file>failed.png</file>
|
||||
<file>succeeded.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
BIN
succeeded.png
Normal file
BIN
succeeded.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
353
threads/importthread.cpp
Normal file
353
threads/importthread.cpp
Normal file
@@ -0,0 +1,353 @@
|
||||
#include "importthread.h"
|
||||
|
||||
#include <QSqlError>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QVariant>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "gzipdevice.h"
|
||||
|
||||
ImportThread::ImportThread(QSqlDatabase &database, const ScanResult &result, const QString &timeFormat, QObject *parent) :
|
||||
QThread(parent),
|
||||
m_database(database),
|
||||
m_result(result),
|
||||
m_timeFormat(timeFormat),
|
||||
m_queryInsertLog(database),
|
||||
m_queryFindHost(database),
|
||||
m_queryInsertHost(database),
|
||||
m_queryFindProcess(database),
|
||||
m_queryInsertProcess(database),
|
||||
m_queryFindFilename(database),
|
||||
m_queryInsertFilename(database),
|
||||
m_queryFindThread(database),
|
||||
m_queryInsertThread(database),
|
||||
m_queryFindType(database),
|
||||
m_queryInsertType(database),
|
||||
m_totalSize(calculateTotalSize())
|
||||
{
|
||||
}
|
||||
|
||||
void ImportThread::run()
|
||||
{
|
||||
{
|
||||
int query { 0 };
|
||||
typedef std::tuple<QString, QSqlQuery&, QString> Row;
|
||||
for (const auto &tuple : {
|
||||
Row(tr("insert log"), m_queryInsertLog, "INSERT INTO `Logs` (`Timestamp`, `HostID`, `ProcessID`, `FilenameID`, `ThreadID`, `TypeID`, `Message`) "
|
||||
"VALUES (:Timestamp, :HostID, :ProcessID, :FilenameID, :ThreadID, :TypeID, :Message);"),
|
||||
Row(tr("find host"), m_queryFindHost, "SELECT `ID` FROM `Hosts` WHERE `Name` = :Name;"),
|
||||
Row(tr("insert host"), m_queryInsertHost, "INSERT INTO `Hosts` (`Name`) VALUES (:Name);"),
|
||||
Row(tr("find host"), m_queryFindProcess, "SELECT `ID` FROM `Processes` WHERE `Name` = :Name;"),
|
||||
Row(tr("insert host"), m_queryInsertProcess, "INSERT INTO `Processes` (`Name`) VALUES (:Name);"),
|
||||
Row(tr("find host"), m_queryFindFilename, "SELECT `ID` FROM `Filenames` WHERE `Name` = :Name;"),
|
||||
Row(tr("insert host"), m_queryInsertFilename, "INSERT INTO `Filenames` (`Name`) VALUES (:Name);"),
|
||||
Row(tr("find host"), m_queryFindThread, "SELECT `ID` FROM `Threads` WHERE `Name` = :Name;"),
|
||||
Row(tr("insert host"), m_queryInsertThread, "INSERT INTO `Threads` (`Name`) VALUES (:Name);"),
|
||||
Row(tr("find host"), m_queryFindType, "SELECT `ID` FROM `Types` WHERE `Name` = :Name;"),
|
||||
Row(tr("insert host"), m_queryInsertType, "INSERT INTO `Types` (`Name`) VALUES (:Name);")
|
||||
})
|
||||
{
|
||||
if (isInterruptionRequested())
|
||||
return;
|
||||
|
||||
emit statusUpdate(tr("Preparing query to %0...").arg(std::get<0>(tuple)));
|
||||
if (!std::get<1>(tuple).prepare(std::get<2>(tuple)))
|
||||
{
|
||||
emit statusUpdate(tr("Failed."));
|
||||
emit logMessage(tr("Could not prepare query to %0: %1").arg(std::get<0>(tuple), std::get<1>(tuple).lastError().text()));
|
||||
return;
|
||||
}
|
||||
emit progressUpdate(query++, 11);
|
||||
}
|
||||
}
|
||||
|
||||
qint64 processedSize { 0 };
|
||||
|
||||
for (auto hostsIter = m_result.constBegin(); hostsIter != m_result.constEnd(); hostsIter++)
|
||||
{
|
||||
if (isInterruptionRequested())
|
||||
return;
|
||||
|
||||
m_queryInsertLog.bindValue(":HostID", getHostID(hostsIter.key()));
|
||||
|
||||
for (auto processesIter = hostsIter.value().constBegin(); processesIter != hostsIter.value().constEnd(); processesIter++)
|
||||
{
|
||||
if (isInterruptionRequested())
|
||||
return;
|
||||
|
||||
m_queryInsertLog.bindValue(":ProcessID", getProcessID(processesIter.key()));
|
||||
|
||||
for (auto datesIter = processesIter.value().constBegin(); datesIter != processesIter.value().constEnd(); datesIter++)
|
||||
{
|
||||
if (isInterruptionRequested())
|
||||
return;
|
||||
|
||||
m_queryInsertLog.bindValue(":FilenameID", getFilenameID(datesIter.value().filename));
|
||||
|
||||
emit logMessage(datesIter.value().filename);
|
||||
|
||||
QFile file(datesIter.value().filepath);
|
||||
QFile::OpenMode flags = QIODevice::ReadOnly;
|
||||
if (!datesIter.value().gzipCompressed)
|
||||
flags |= QIODevice::Text;
|
||||
if (!file.open(flags))
|
||||
{
|
||||
emit logMessage(tr("Could not open logfile: %0").arg(file.errorString()));
|
||||
continue;
|
||||
}
|
||||
|
||||
struct {
|
||||
QDateTime dateTime;
|
||||
QString thread { "main" };
|
||||
QString type;
|
||||
QString message;
|
||||
} test;
|
||||
|
||||
const auto insert = [&test,this,&processedSize,&file](){
|
||||
m_queryInsertLog.bindValue(":Timestamp", test.dateTime.toString(QStringLiteral("yyyy-MM-dd HH:mm:ss.zzz")));
|
||||
m_queryInsertLog.bindValue(":ThreadID", getThreadID(test.thread));
|
||||
m_queryInsertLog.bindValue(":TypeID", getTypeID(test.type));
|
||||
m_queryInsertLog.bindValue(":Message", test.message);
|
||||
if (!m_queryInsertLog.exec())
|
||||
emit logMessage(tr("could not execute query to insert log: %0").arg(m_queryInsertLog.lastError().text()));
|
||||
|
||||
m_logsInserted++;
|
||||
const auto now = QDateTime::currentDateTime();
|
||||
if (m_lastProgressUpdate.isNull() || m_lastProgressUpdate.msecsTo(now) >= 100)
|
||||
{
|
||||
emit statusUpdate(tr("%0 logs inserted...").arg(m_logsInserted));
|
||||
emit progressUpdate(processedSize + file.pos(), m_totalSize);
|
||||
m_lastProgressUpdate = now;
|
||||
}
|
||||
};
|
||||
|
||||
int indentionOffset;
|
||||
|
||||
if (!m_database.transaction())
|
||||
{
|
||||
emit statusUpdate(tr("Aborted."));
|
||||
emit logMessage(tr("Could not start transaction: %0").arg(m_database.lastError().text()));
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
const std::unique_ptr<GzipDevice> gzipProxy([&datesIter,&file](){
|
||||
if (datesIter.value().gzipCompressed)
|
||||
return std::make_unique<GzipDevice>(file);
|
||||
return std::unique_ptr<GzipDevice>{};
|
||||
}());
|
||||
|
||||
QTextStream textStream(gzipProxy != nullptr ? static_cast<QIODevice*>(gzipProxy.get()) : &file);
|
||||
while(!textStream.atEnd())
|
||||
{
|
||||
if (isInterruptionRequested())
|
||||
{
|
||||
if (!m_database.rollback())
|
||||
{
|
||||
emit statusUpdate(tr("Aborted."));
|
||||
emit logMessage(tr("Could not rollback transaction: %0").arg(m_database.lastError().text()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto line = textStream.readLine();
|
||||
const auto match = m_lineRegex.match(line);
|
||||
if (match.hasMatch())
|
||||
{
|
||||
if (!test.dateTime.isNull())
|
||||
insert();
|
||||
|
||||
test.message = match.captured(4);
|
||||
|
||||
{
|
||||
const auto threadMatch = m_threadRegex.match(test.message);
|
||||
if (threadMatch.hasMatch())
|
||||
test.thread = threadMatch.captured(1);
|
||||
}
|
||||
|
||||
test.dateTime = { datesIter.key(), QTime::fromString(match.captured(2), m_timeFormat) };
|
||||
|
||||
test.type = match.captured(3);
|
||||
test.type = test.type.left(test.type.indexOf(':'));
|
||||
|
||||
indentionOffset = match.captured(1).length();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!test.dateTime.isNull())
|
||||
{
|
||||
if (line.length() >= indentionOffset &&
|
||||
std::all_of(line.constBegin(), line.constBegin() + indentionOffset, [](const QChar &c){ return c == ' '; }))
|
||||
line.remove(0, indentionOffset);
|
||||
|
||||
test.message.append("\n");
|
||||
test.message.append(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!test.dateTime.isNull())
|
||||
insert();
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
emit logMessage(tr("Aborted: %0").arg(ex.what()));
|
||||
}
|
||||
|
||||
if (!m_database.commit())
|
||||
{
|
||||
emit statusUpdate(tr("Aborted."));
|
||||
emit logMessage(tr("Could not commit transaction: %0").arg(m_database.lastError().text()));
|
||||
return;
|
||||
}
|
||||
|
||||
processedSize += file.size();
|
||||
emit statusUpdate(tr("%0 logs inserted...").arg(m_logsInserted));
|
||||
emit progressUpdate(processedSize, m_totalSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ImportThread::getHostID(const QString &host)
|
||||
{
|
||||
const auto iter = m_hosts.find(host);
|
||||
if (iter != m_hosts.constEnd())
|
||||
return *iter;
|
||||
|
||||
m_queryFindHost.bindValue(":Name", host);
|
||||
if (!m_queryFindHost.exec())
|
||||
qFatal("could not execute query to find host: %s", qPrintable(m_queryFindHost.lastError().text()));
|
||||
|
||||
if (m_queryFindHost.next())
|
||||
{
|
||||
const auto id = m_queryFindHost.value(0).toInt();
|
||||
m_hosts.insert(host, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
m_queryInsertHost.bindValue(":Name", host);
|
||||
if (!m_queryInsertHost.exec())
|
||||
qFatal("could not execute query to insert host: %s", qPrintable(m_queryInsertHost.lastError().text()));
|
||||
|
||||
const auto id = m_queryInsertHost.lastInsertId().toInt();
|
||||
m_hosts.insert(host, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
int ImportThread::getProcessID(const QString &process)
|
||||
{
|
||||
const auto iter = m_processes.find(process);
|
||||
if (iter != m_processes.constEnd())
|
||||
return *iter;
|
||||
|
||||
m_queryFindProcess.bindValue(":Name", process);
|
||||
if (!m_queryFindProcess.exec())
|
||||
qFatal("could not execute query to find process: %s", qPrintable(m_queryFindProcess.lastError().text()));
|
||||
|
||||
if (m_queryFindProcess.next())
|
||||
{
|
||||
const auto id = m_queryFindProcess.value(0).toInt();
|
||||
m_processes.insert(process, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
m_queryInsertProcess.bindValue(":Name", process);
|
||||
if (!m_queryInsertProcess.exec())
|
||||
qFatal("could not execute query to insert process: %s", qPrintable(m_queryInsertProcess.lastError().text()));
|
||||
|
||||
const auto id = m_queryInsertProcess.lastInsertId().toInt();
|
||||
m_processes.insert(process, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
int ImportThread::getFilenameID(const QString &filename)
|
||||
{
|
||||
const auto iter = m_filenames.find(filename);
|
||||
if (iter != m_filenames.constEnd())
|
||||
return *iter;
|
||||
|
||||
m_queryFindFilename.bindValue(":Name", filename);
|
||||
if (!m_queryFindFilename.exec())
|
||||
qFatal("could not execute query to find filename: %s", qPrintable(m_queryFindFilename.lastError().text()));
|
||||
|
||||
if (m_queryFindFilename.next())
|
||||
{
|
||||
const auto id = m_queryFindFilename.value(0).toInt();
|
||||
m_filenames.insert(filename, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
m_queryInsertFilename.bindValue(":Name", filename);
|
||||
if (!m_queryInsertFilename.exec())
|
||||
qFatal("could not execute query to insert filename: %s", qPrintable(m_queryInsertFilename.lastError().text()));
|
||||
|
||||
const auto id = m_queryInsertFilename.lastInsertId().toInt();
|
||||
m_filenames.insert(filename, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
int ImportThread::getThreadID(const QString &thread)
|
||||
{
|
||||
const auto iter = m_threads.find(thread);
|
||||
if (iter != m_threads.constEnd())
|
||||
return *iter;
|
||||
|
||||
m_queryFindThread.bindValue(":Name", thread);
|
||||
if (!m_queryFindThread.exec())
|
||||
qFatal("could not execute query to find thread: %s", qPrintable(m_queryFindThread.lastError().text()));
|
||||
|
||||
if (m_queryFindThread.next())
|
||||
{
|
||||
const auto id = m_queryFindThread.value(0).toInt();
|
||||
m_threads.insert(thread, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
m_queryInsertThread.bindValue(":Name", thread);
|
||||
if (!m_queryInsertThread.exec())
|
||||
qFatal("could not execute query to insert thread: %s", qPrintable(m_queryInsertThread.lastError().text()));
|
||||
|
||||
const auto id = m_queryInsertThread.lastInsertId().toInt();
|
||||
m_threads.insert(thread, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
int ImportThread::getTypeID(const QString &type)
|
||||
{
|
||||
const auto iter = m_types.find(type);
|
||||
if (iter != m_types.constEnd())
|
||||
return *iter;
|
||||
|
||||
m_queryFindType.bindValue(":Name", type);
|
||||
if (!m_queryFindType.exec())
|
||||
qFatal("could not execute query to find type: %s", qPrintable(m_queryFindType.lastError().text()));
|
||||
|
||||
if (m_queryFindType.next())
|
||||
{
|
||||
const auto id = m_queryFindType.value(0).toInt();
|
||||
m_types.insert(type, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
m_queryInsertType.bindValue(":Name", type);
|
||||
if (!m_queryInsertType.exec())
|
||||
qFatal("could not execute query to insert type: %s", qPrintable(m_queryInsertType.lastError().text()));
|
||||
|
||||
const auto id = m_queryInsertType.lastInsertId().toInt();
|
||||
m_types.insert(type, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
qint64 ImportThread::calculateTotalSize() const
|
||||
{
|
||||
qint64 totalSize { 0 };
|
||||
for (auto hostsIter = m_result.constBegin(); hostsIter != m_result.constEnd(); hostsIter++)
|
||||
for (auto processesIter = hostsIter.value().constBegin(); processesIter != hostsIter.value().constEnd(); processesIter++)
|
||||
for (auto datesIter = processesIter.value().constBegin(); datesIter != processesIter.value().constEnd(); datesIter++)
|
||||
totalSize += datesIter.value().filesize;
|
||||
return totalSize;
|
||||
}
|
63
threads/importthread.h
Normal file
63
threads/importthread.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <QThread>
|
||||
#include <QSqlQuery>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
class QSqlDatabase;
|
||||
|
||||
class ImportThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ImportThread(QSqlDatabase &database, const ScanResult &result, const QString &timeFormat, QObject *parent = nullptr);
|
||||
|
||||
signals:
|
||||
void statusUpdate(const QString &message);
|
||||
void progressUpdate(qint64 finished, qint64 total);
|
||||
void logMessage(const QString &message);
|
||||
|
||||
protected:
|
||||
void run() override;
|
||||
|
||||
private:
|
||||
int getHostID(const QString &host);
|
||||
int getProcessID(const QString &process);
|
||||
int getFilenameID(const QString &filename);
|
||||
int getThreadID(const QString &thread);
|
||||
int getTypeID(const QString &type);
|
||||
|
||||
qint64 calculateTotalSize() const;
|
||||
|
||||
QSqlDatabase &m_database;
|
||||
ScanResult m_result;
|
||||
QString m_timeFormat;
|
||||
|
||||
QHash<QString, int> m_hosts;
|
||||
QHash<QString, int> m_processes;
|
||||
QHash<QString, int> m_filenames;
|
||||
QHash<QString, int> m_threads;
|
||||
QHash<QString, int> m_types;
|
||||
|
||||
QSqlQuery m_queryInsertLog;
|
||||
QSqlQuery m_queryFindHost;
|
||||
QSqlQuery m_queryInsertHost;
|
||||
QSqlQuery m_queryFindProcess;
|
||||
QSqlQuery m_queryInsertProcess;
|
||||
QSqlQuery m_queryFindFilename;
|
||||
QSqlQuery m_queryInsertFilename;
|
||||
QSqlQuery m_queryFindThread;
|
||||
QSqlQuery m_queryInsertThread;
|
||||
QSqlQuery m_queryFindType;
|
||||
QSqlQuery m_queryInsertType;
|
||||
|
||||
const qint64 m_totalSize;
|
||||
quint64 m_logsInserted { 0 };
|
||||
QDateTime m_lastProgressUpdate;
|
||||
|
||||
const QRegularExpression m_lineRegex { "^(([0-9]+:[0-9]+:[0-9]+(?:\\.[0-9]+)?) (FATAL: |ERROR: |WARNING: |INFO: |DEBUG: |DEBUG1: |DEBUG2: ))(.*)$" };
|
||||
const QRegularExpression m_threadRegex { "----- Thread context: (.*) -----$" };
|
||||
};
|
86
threads/remotescannerthread.cpp
Normal file
86
threads/remotescannerthread.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#include "remotescannerthread.h"
|
||||
|
||||
#include <QDirIterator>
|
||||
#include <QFileInfo>
|
||||
#include <QStringBuilder>
|
||||
|
||||
RemoteScannerThread::RemoteScannerThread(const QString &dir, QObject *parent) :
|
||||
QThread(parent),
|
||||
m_dir(dir)
|
||||
{
|
||||
}
|
||||
|
||||
void RemoteScannerThread::run()
|
||||
{
|
||||
scanForHosts();
|
||||
}
|
||||
|
||||
void RemoteScannerThread::scanForHosts()
|
||||
{
|
||||
if (m_files)
|
||||
qFatal("thread was already run");
|
||||
|
||||
QDirIterator iter(m_dir, QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
while (iter.hasNext())
|
||||
{
|
||||
if (isInterruptionRequested())
|
||||
return;
|
||||
|
||||
const QFileInfo fileInfo(iter.next());
|
||||
|
||||
const auto host = fileInfo.fileName();
|
||||
|
||||
emit logMessage(tr("Scanning host %0...").arg(host));
|
||||
|
||||
scanForLogfiles(host, fileInfo.absoluteFilePath());
|
||||
}
|
||||
|
||||
emit progressUpdate(m_files, m_files - m_valid);
|
||||
emit finished(m_result);
|
||||
}
|
||||
|
||||
void RemoteScannerThread::scanForLogfiles(const QString &hostname, const QString &hostDir)
|
||||
{
|
||||
auto &hostEntry = m_result[hostname];
|
||||
|
||||
QDirIterator dirIter(hostDir, { "*.log.gz" }, QDir::Files);
|
||||
while (dirIter.hasNext())
|
||||
{
|
||||
if (isInterruptionRequested())
|
||||
return;
|
||||
|
||||
const QFileInfo fileInfo(dirIter.next());
|
||||
|
||||
m_files++;
|
||||
|
||||
const auto match = m_fileExpression.match(fileInfo.fileName());
|
||||
if (!match.hasMatch())
|
||||
continue;
|
||||
|
||||
const auto process = match.captured(1);
|
||||
if (process.endsWith(QStringLiteral(".olog")))
|
||||
continue;
|
||||
|
||||
const auto date = QDate::fromString(match.captured(2), QStringLiteral("yyyyMMdd"));
|
||||
if (date.isNull())
|
||||
continue;
|
||||
|
||||
m_valid++;
|
||||
|
||||
Q_ASSERT(!hostEntry[process].contains(date));
|
||||
|
||||
hostEntry[process].insert(date, {
|
||||
hostname % "/" % fileInfo.fileName(),
|
||||
fileInfo.absoluteFilePath(),
|
||||
fileInfo.size(),
|
||||
true
|
||||
});
|
||||
|
||||
const auto now = QDateTime::currentDateTime();
|
||||
if (m_lastUpdate.isNull() || m_lastUpdate.msecsTo(now) >= 100)
|
||||
{
|
||||
emit progressUpdate(m_files, m_files - m_valid);
|
||||
m_lastUpdate = now;
|
||||
}
|
||||
}
|
||||
}
|
40
threads/remotescannerthread.h
Normal file
40
threads/remotescannerthread.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <QThread>
|
||||
#include <QRegularExpression>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <QDate>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
class RemoteScannerThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
const QRegularExpression m_fileExpression { "^(.+)-([0-9]{8})\\.log?" };
|
||||
|
||||
public:
|
||||
RemoteScannerThread(const QString &dir, QObject *parent = nullptr);
|
||||
|
||||
signals:
|
||||
void progressUpdate(int totalFiles, int skippedFiles);
|
||||
void logMessage(const QString &message);
|
||||
void finished(const ScanResult &result);
|
||||
|
||||
protected:
|
||||
void run() override;
|
||||
|
||||
private:
|
||||
void scanForHosts();
|
||||
void scanForLogfiles(const QString &hostname, const QString &hostDir);
|
||||
|
||||
QString m_dir;
|
||||
|
||||
ScanResult m_result;
|
||||
|
||||
int m_files { 0 };
|
||||
int m_valid { 0 };
|
||||
|
||||
QDateTime m_lastUpdate;
|
||||
};
|
101
threads/tablecreatorthread.cpp
Normal file
101
threads/tablecreatorthread.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#include "tablecreatorthread.h"
|
||||
|
||||
#include <QSqlDatabase>
|
||||
#include <QSqlQuery>
|
||||
#include <QSqlError>
|
||||
#include <QDebug>
|
||||
|
||||
const QStringList TableCreatorThread::m_tables { "Hosts", "Processes", "Filenames", "Threads", "Types", "Logs" };
|
||||
|
||||
TableCreatorThread::TableCreatorThread(QSqlDatabase &database, QObject *parent) :
|
||||
QThread(parent), m_database(database)
|
||||
{
|
||||
}
|
||||
|
||||
const QStringList &TableCreatorThread::tables()
|
||||
{
|
||||
return m_tables;
|
||||
}
|
||||
|
||||
void TableCreatorThread::run()
|
||||
{
|
||||
int index { 0 };
|
||||
|
||||
for (const QString tableName : m_tables)
|
||||
{
|
||||
if (isInterruptionRequested())
|
||||
return;
|
||||
|
||||
const auto sql = [&tableName,type=m_database.driverName()]() -> QString {
|
||||
if (tableName == "Logs")
|
||||
{
|
||||
if (type == "QSQLITE")
|
||||
{
|
||||
return "CREATE TABLE IF NOT EXISTS `Logs` ("
|
||||
"`ID` INTEGER PRIMARY KEY, "
|
||||
"`Timestamp` TEXT NOT NULL, "
|
||||
"`HostID` INTEGER NOT NULL, "
|
||||
"`ProcessID` INTEGER NOT NULL, "
|
||||
"`FilenameID` INTEGER NOT NULL, "
|
||||
"`ThreadID` INTEGER NOT NULL, "
|
||||
"`TypeID` INTEGER NOT NULL, "
|
||||
"`Message` TEXT NOT NULL, "
|
||||
"FOREIGN KEY (`HostID`) REFERENCES `Hosts`(`ID`), "
|
||||
"FOREIGN KEY (`ProcessID`) REFERENCES `Processes`(`ID`), "
|
||||
"FOREIGN KEY (`FilenameID`) REFERENCES `Filenames`(`ID`), "
|
||||
"FOREIGN KEY (`ThreadID`) REFERENCES `Threads`(`ID`), "
|
||||
"FOREIGN KEY (`TypeID`) REFERENCES `Types`(`ID`)"
|
||||
");";
|
||||
}
|
||||
else if (type == "QMYSQL")
|
||||
{
|
||||
return "CREATE TABLE IF NOT EXISTS `Logs` ("
|
||||
"`ID` INT UNSIGNED NOT NULL AUTO_INCREMENT, "
|
||||
"`Timestamp` DATETIME NOT NULL, "
|
||||
"`HostID` INT UNSIGNED NOT NULL, "
|
||||
"`ProcessID` INT UNSIGNED NOT NULL, "
|
||||
"`FilenameID` INT UNSIGNED NOT NULL, "
|
||||
"`ThreadID` INT UNSIGNED NOT NULL, "
|
||||
"`TypeID` INT UNSIGNED NOT NULL, "
|
||||
"`Message` LONGTEXT NOT NULL, "
|
||||
"PRIMARY KEY(`ID`), "
|
||||
"INDEX(`Timestamp`), "
|
||||
"FOREIGN KEY (`HostID`) REFERENCES `Hosts`(`ID`), "
|
||||
"FOREIGN KEY (`ProcessID`) REFERENCES `Processes`(`ID`), "
|
||||
"FOREIGN KEY (`FilenameID`) REFERENCES `Filenames`(`ID`), "
|
||||
"FOREIGN KEY (`ThreadID`) REFERENCES `Threads`(`ID`), "
|
||||
"FOREIGN KEY (`TypeID`) REFERENCES `Types`(`ID`)"
|
||||
");";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (type == "QSQLITE")
|
||||
{
|
||||
return QString("CREATE TABLE IF NOT EXISTS `%0` ("
|
||||
"`ID` INTEGER PRIMARY KEY, "
|
||||
"`Name` TEXT NOT NULL UNIQUE"
|
||||
");").arg(tableName);
|
||||
}
|
||||
else if (type == "QMYSQL")
|
||||
{
|
||||
return QString("CREATE TABLE IF NOT EXISTS `%0` ("
|
||||
"`ID` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, "
|
||||
"`Name` VARCHAR(255) NOT NULL UNIQUE"
|
||||
");").arg(tableName);
|
||||
}
|
||||
}
|
||||
|
||||
qFatal("unknown database type %s", qPrintable(type));
|
||||
}();
|
||||
|
||||
QSqlQuery query(sql, m_database);
|
||||
if (query.lastError().isValid())
|
||||
{
|
||||
qCritical() << query.lastError().text();
|
||||
return;
|
||||
}
|
||||
|
||||
emit someSignal(index++);
|
||||
}
|
||||
}
|
26
threads/tablecreatorthread.h
Normal file
26
threads/tablecreatorthread.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <QThread>
|
||||
|
||||
class QSqlDatabase;
|
||||
|
||||
class TableCreatorThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
static const QStringList m_tables;
|
||||
|
||||
public:
|
||||
explicit TableCreatorThread(QSqlDatabase &database, QObject *parent = nullptr);
|
||||
|
||||
static const QStringList &tables();
|
||||
|
||||
signals:
|
||||
void someSignal(int index);
|
||||
|
||||
protected:
|
||||
void run() override;
|
||||
|
||||
private:
|
||||
QSqlDatabase &m_database;
|
||||
};
|
104
widgets/databasewidget.cpp
Normal file
104
widgets/databasewidget.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#include "databasewidget.h"
|
||||
#include "ui_databasewidget.h"
|
||||
|
||||
DatabaseWidget::DatabaseWidget(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
m_ui(std::make_unique<Ui::DatabaseWidget>())
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
m_ui->comboBox->addItem(tr("SQLite"), "QSQLITE");
|
||||
m_ui->comboBox->addItem(tr("MySQL"), "QMYSQL");
|
||||
|
||||
// for debugging
|
||||
setDriver("QMYSQL");
|
||||
// setMysqlHostname("sql7.freemysqlhosting.net");
|
||||
// setMysqlUsername("sql7285815");
|
||||
// setMysqlPassword("BKhysrtqKl");
|
||||
// setMysqlDatabase("sql7285815");
|
||||
|
||||
//setMysqlHostname("brunner.ninja");
|
||||
setMysqlHostname("localhost");
|
||||
setMysqlUsername("logtest");
|
||||
setMysqlPassword("logtest");
|
||||
setMysqlDatabase("logtest");
|
||||
}
|
||||
|
||||
DatabaseWidget::~DatabaseWidget() = default;
|
||||
|
||||
QString DatabaseWidget::driver() const
|
||||
{
|
||||
return m_ui->comboBox->currentData().toString();
|
||||
}
|
||||
|
||||
void DatabaseWidget::setDriver(const QString &driver)
|
||||
{
|
||||
m_ui->comboBox->setCurrentIndex(m_ui->comboBox->findData(driver));
|
||||
}
|
||||
|
||||
QString DatabaseWidget::sqliteFilepath() const
|
||||
{
|
||||
return m_ui->fileSelectionWidget->path();
|
||||
}
|
||||
|
||||
void DatabaseWidget::setSqliteFilepath(const QString &sqliteFilepath)
|
||||
{
|
||||
m_ui->fileSelectionWidget->setPath(sqliteFilepath);
|
||||
}
|
||||
|
||||
QString DatabaseWidget::mysqlHostname() const
|
||||
{
|
||||
return m_ui->lineEditHostname->text();
|
||||
}
|
||||
|
||||
void DatabaseWidget::setMysqlHostname(const QString &mysqlHostname)
|
||||
{
|
||||
m_ui->lineEditHostname->setText(mysqlHostname);
|
||||
}
|
||||
|
||||
QString DatabaseWidget::mysqlUsername() const
|
||||
{
|
||||
return m_ui->lineEditUsername->text();
|
||||
}
|
||||
|
||||
void DatabaseWidget::setMysqlUsername(const QString &mysqlUsername)
|
||||
{
|
||||
m_ui->lineEditUsername->setText(mysqlUsername);
|
||||
}
|
||||
|
||||
QString DatabaseWidget::mysqlPassword() const
|
||||
{
|
||||
return m_ui->lineEditPassword->text();
|
||||
}
|
||||
|
||||
void DatabaseWidget::setMysqlPassword(const QString &mysqlPassword)
|
||||
{
|
||||
m_ui->lineEditPassword->setText(mysqlPassword);
|
||||
}
|
||||
|
||||
QString DatabaseWidget::mysqlDatabase() const
|
||||
{
|
||||
return m_ui->lineEditDatabase->text();
|
||||
}
|
||||
|
||||
void DatabaseWidget::setMysqlDatabase(const QString &mysqlDatabase)
|
||||
{
|
||||
m_ui->lineEditDatabase->setText(mysqlDatabase);
|
||||
}
|
||||
|
||||
QSqlDatabase DatabaseWidget::createConnection(const QString &connectionName)
|
||||
{
|
||||
auto db = QSqlDatabase::addDatabase(driver(), connectionName);
|
||||
|
||||
if (db.driverName() == "QSQLITE")
|
||||
db.setDatabaseName(sqliteFilepath());
|
||||
else
|
||||
{
|
||||
db.setHostName(mysqlHostname());
|
||||
db.setUserName(mysqlUsername());
|
||||
db.setPassword(mysqlPassword());
|
||||
db.setDatabaseName(mysqlDatabase());
|
||||
}
|
||||
|
||||
return db;
|
||||
}
|
40
widgets/databasewidget.h
Normal file
40
widgets/databasewidget.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QSqlDatabase>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Ui { class DatabaseWidget; }
|
||||
|
||||
class DatabaseWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DatabaseWidget(QWidget *parent = nullptr);
|
||||
~DatabaseWidget() override;
|
||||
|
||||
QString driver() const;
|
||||
void setDriver(const QString &driver);
|
||||
|
||||
QString sqliteFilepath() const;
|
||||
void setSqliteFilepath(const QString &sqliteFilepath);
|
||||
|
||||
QString mysqlHostname() const;
|
||||
void setMysqlHostname(const QString &mysqlHostname);
|
||||
|
||||
QString mysqlUsername() const;
|
||||
void setMysqlUsername(const QString &mysqlUsername);
|
||||
|
||||
QString mysqlPassword() const;
|
||||
void setMysqlPassword(const QString &mysqlPassword);
|
||||
|
||||
QString mysqlDatabase() const;
|
||||
void setMysqlDatabase(const QString &mysqlDatabase);
|
||||
|
||||
QSqlDatabase createConnection(const QString& connectionName = QLatin1String(QSqlDatabase::defaultConnection));
|
||||
|
||||
private:
|
||||
const std::unique_ptr<Ui::DatabaseWidget> m_ui;
|
||||
};
|
124
widgets/databasewidget.ui
Normal file
124
widgets/databasewidget.ui
Normal file
@@ -0,0 +1,124 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>DatabaseWidget</class>
|
||||
<widget class="QWidget" name="DatabaseWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>175</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,1">
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBox"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="stackedWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="page">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="FileSelectionWidget" name="fileSelectionWidget" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_2">
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="lineEditUsername"/>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="lineEditHostname"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelHostname">
|
||||
<property name="text">
|
||||
<string><b>Hostname:</b></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="labelUsername">
|
||||
<property name="text">
|
||||
<string><b>Username:</b></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="labelPassword">
|
||||
<property name="text">
|
||||
<string><b>Password:</b></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="labelDatabase">
|
||||
<property name="text">
|
||||
<string><b>Database:</b></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="lineEditPassword">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="lineEditDatabase"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>FileSelectionWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>widgets/fileselectionwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>comboBox</sender>
|
||||
<signal>currentIndexChanged(int)</signal>
|
||||
<receiver>stackedWidget</receiver>
|
||||
<slot>setCurrentIndex(int)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>199</x>
|
||||
<y>20</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>199</x>
|
||||
<y>101</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
75
widgets/fileselectionwidget.cpp
Normal file
75
widgets/fileselectionwidget.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#include "fileselectionwidget.h"
|
||||
#include "ui_fileselectionwidget.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
|
||||
FileSelectionWidget::FileSelectionWidget(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
m_ui(std::make_unique<Ui::FileSelectionWidget>()),
|
||||
m_mode(Mode::OpenFile)
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
connect(m_ui->lineEdit, &QLineEdit::textChanged, this, &FileSelectionWidget::pathChanged);
|
||||
connect(m_ui->pushButton, &QAbstractButton::pressed, this, &FileSelectionWidget::selectPath);
|
||||
}
|
||||
|
||||
FileSelectionWidget::FileSelectionWidget(const Mode mode, QWidget *parent) :
|
||||
QWidget(parent),
|
||||
m_ui(std::make_unique<Ui::FileSelectionWidget>()),
|
||||
m_mode(mode)
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
connect(m_ui->lineEdit, &QLineEdit::textChanged, this, &FileSelectionWidget::pathChanged);
|
||||
connect(m_ui->pushButton, &QAbstractButton::pressed, this, &FileSelectionWidget::selectPath);
|
||||
}
|
||||
|
||||
FileSelectionWidget::FileSelectionWidget(const Mode mode, const QString &path, QWidget *parent) :
|
||||
QWidget(parent),
|
||||
m_ui(std::make_unique<Ui::FileSelectionWidget>()),
|
||||
m_mode(mode)
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
m_ui->lineEdit->setText(path);
|
||||
|
||||
connect(m_ui->lineEdit, &QLineEdit::textChanged, this, &FileSelectionWidget::pathChanged);
|
||||
connect(m_ui->pushButton, &QAbstractButton::pressed, this, &FileSelectionWidget::selectPath);
|
||||
}
|
||||
|
||||
FileSelectionWidget::~FileSelectionWidget() = default;
|
||||
|
||||
FileSelectionWidget::Mode FileSelectionWidget::mode() const
|
||||
{
|
||||
return m_mode;
|
||||
}
|
||||
|
||||
void FileSelectionWidget::setMode(const FileSelectionWidget::Mode mode)
|
||||
{
|
||||
m_mode = mode;
|
||||
}
|
||||
|
||||
QString FileSelectionWidget::path() const
|
||||
{
|
||||
return m_ui->lineEdit->text();
|
||||
}
|
||||
|
||||
void FileSelectionWidget::setPath(const QString &path)
|
||||
{
|
||||
m_ui->lineEdit->setText(path);
|
||||
}
|
||||
|
||||
void FileSelectionWidget::selectPath()
|
||||
{
|
||||
QString path;
|
||||
switch (m_mode)
|
||||
{
|
||||
case Mode::OpenFile: path = QFileDialog::getOpenFileName(this); break;
|
||||
case Mode::SaveFile: path = QFileDialog::getSaveFileName(this); break;
|
||||
case Mode::ExistingDirectory: path = QFileDialog::getExistingDirectory(this); break;
|
||||
}
|
||||
|
||||
if (!path.isEmpty())
|
||||
m_ui->lineEdit->setText(path);
|
||||
}
|
38
widgets/fileselectionwidget.h
Normal file
38
widgets/fileselectionwidget.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Ui { class FileSelectionWidget; }
|
||||
|
||||
class FileSelectionWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum class Mode {
|
||||
OpenFile, SaveFile, ExistingDirectory
|
||||
};
|
||||
|
||||
explicit FileSelectionWidget(QWidget *parent = nullptr);
|
||||
FileSelectionWidget(const Mode mode, QWidget *parent = nullptr);
|
||||
FileSelectionWidget(const Mode mode, const QString &path, QWidget *parent = nullptr);
|
||||
~FileSelectionWidget() override;
|
||||
|
||||
Mode mode() const;
|
||||
void setMode(const Mode mode);
|
||||
|
||||
QString path() const;
|
||||
void setPath(const QString &path);
|
||||
|
||||
signals:
|
||||
void pathChanged(const QString &path);
|
||||
|
||||
private slots:
|
||||
void selectPath();
|
||||
|
||||
private:
|
||||
const std::unique_ptr<Ui::FileSelectionWidget> m_ui;
|
||||
Mode m_mode;
|
||||
};
|
31
widgets/fileselectionwidget.ui
Normal file
31
widgets/fileselectionwidget.ui
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>FileSelectionWidget</class>
|
||||
<widget class="QWidget" name="FileSelectionWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>41</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEdit"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton">
|
||||
<property name="text">
|
||||
<string>Select...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
54
wizard/conclusionpage.cpp
Normal file
54
wizard/conclusionpage.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include "conclusionpage.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QCheckBox>
|
||||
#include <QSqlQuery>
|
||||
#include <QSqlError>
|
||||
#include <QMessageBox>
|
||||
#include <QStringBuilder>
|
||||
#include <QVariant>
|
||||
|
||||
#include "importwizard.h"
|
||||
|
||||
ConclusionPage::ConclusionPage(QWidget *parent) :
|
||||
QWizardPage(parent)
|
||||
{
|
||||
setTitle(tr("Conclusion"));
|
||||
setSubTitle(tr("Import successfully finished!"));
|
||||
|
||||
auto layout = new QVBoxLayout;
|
||||
|
||||
m_label = new QLabel;
|
||||
layout->addWidget(m_label);
|
||||
|
||||
layout->addStretch(1);
|
||||
|
||||
m_checkBox = new QCheckBox(tr("Open new database"));
|
||||
m_checkBox->setChecked(true);
|
||||
layout->addWidget(m_checkBox);
|
||||
registerField("open", m_checkBox);
|
||||
|
||||
layout->addStretch(1);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void ConclusionPage::initializePage()
|
||||
{
|
||||
auto importWizard = qobject_cast<ImportWizard*>(wizard());
|
||||
Q_ASSERT(importWizard);
|
||||
Q_ASSERT(importWizard->database().isOpen());
|
||||
|
||||
QSqlQuery query("SELECT COUNT(*) FROM `Logs`;", importWizard->database());
|
||||
if (query.lastError().isValid())
|
||||
{
|
||||
QMessageBox::warning(nullptr, tr("Could not get count!"), tr("Could not get count!") % "\n\n" % query.lastError().text());
|
||||
return;
|
||||
}
|
||||
|
||||
const auto fetched = query.next();
|
||||
Q_ASSERT(fetched);
|
||||
|
||||
m_label->setText(tr("<b>%0 rows</b> have been imported.").arg(query.value(0).toInt()));
|
||||
}
|
20
wizard/conclusionpage.h
Normal file
20
wizard/conclusionpage.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWizardPage>
|
||||
|
||||
class QLabel;
|
||||
class QCheckBox;
|
||||
|
||||
class ConclusionPage : public QWizardPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConclusionPage(QWidget *parent = nullptr);
|
||||
|
||||
void initializePage() override;
|
||||
|
||||
private:
|
||||
QLabel *m_label;
|
||||
QCheckBox *m_checkBox;
|
||||
};
|
46
wizard/databasepage.cpp
Normal file
46
wizard/databasepage.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "databasepage.h"
|
||||
#include "ui_databasepage.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QSqlError>
|
||||
#include <QMessageBox>
|
||||
#include <QStringBuilder>
|
||||
#include <QSqlQuery>
|
||||
#include <QFileDialog>
|
||||
|
||||
#include "widgets/databasewidget.h"
|
||||
#include "importwizard.h"
|
||||
|
||||
DatabasePage::DatabasePage(QWidget *parent) :
|
||||
QWizardPage(parent),
|
||||
m_ui(std::make_unique<Ui::DatabasePage>())
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
setCommitPage(true);
|
||||
}
|
||||
|
||||
DatabasePage::~DatabasePage() = default;
|
||||
|
||||
int DatabasePage::nextId() const
|
||||
{
|
||||
return int(ImportWizard::Pages::Tables);
|
||||
}
|
||||
|
||||
bool DatabasePage::validatePage()
|
||||
{
|
||||
auto importWizard = qobject_cast<ImportWizard*>(wizard());
|
||||
Q_ASSERT(importWizard);
|
||||
Q_ASSERT(!importWizard->database().isOpen());
|
||||
|
||||
importWizard->database() = m_ui->databaseWidget->createConnection();
|
||||
|
||||
if (!importWizard->database().open())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Could not open database!"), tr("Could not open database!") % "\n\n" % importWizard->database().lastError().text());
|
||||
importWizard->database() = {};
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
23
wizard/databasepage.h
Normal file
23
wizard/databasepage.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWizardPage>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Ui { class DatabasePage; }
|
||||
|
||||
class DatabasePage : public QWizardPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DatabasePage(QWidget *parent = nullptr);
|
||||
~DatabasePage() override;
|
||||
|
||||
int nextId() const override;
|
||||
|
||||
bool validatePage() override;
|
||||
|
||||
private:
|
||||
const std::unique_ptr<Ui::DatabasePage> m_ui;
|
||||
};
|
51
wizard/databasepage.ui
Normal file
51
wizard/databasepage.ui
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>DatabasePage</class>
|
||||
<widget class="QWizardPage" name="DatabasePage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>WizardPage</string>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Database</string>
|
||||
</property>
|
||||
<property name="subTitle">
|
||||
<string>Please setup the database connection.</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,1">
|
||||
<item>
|
||||
<widget class="DatabaseWidget" name="databaseWidget" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>263</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>DatabaseWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>widgets/databasewidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
117
wizard/importprogresspage.cpp
Normal file
117
wizard/importprogresspage.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
#include "importprogresspage.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QProgressBar>
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
#include "importwizard.h"
|
||||
#include "threads/importthread.h"
|
||||
|
||||
ImportProgressPage::ImportProgressPage(QWidget *parent) :
|
||||
QWizardPage(parent)
|
||||
{
|
||||
setTitle(tr("Import Progress"));
|
||||
setSubTitle(tr("TODO..."));
|
||||
|
||||
auto layout = new QVBoxLayout;
|
||||
|
||||
{
|
||||
auto hboxLayout = new QHBoxLayout;
|
||||
|
||||
m_labelIcon = new QLabel;
|
||||
hboxLayout->addWidget(m_labelIcon);
|
||||
|
||||
m_labelStatus = new QLabel;
|
||||
hboxLayout->addWidget(m_labelStatus, 1);
|
||||
|
||||
layout->addLayout(hboxLayout);
|
||||
}
|
||||
|
||||
m_progressBar = new QProgressBar;
|
||||
m_progressBar->setMaximum(100);
|
||||
layout->addWidget(m_progressBar);
|
||||
|
||||
m_logView = new QPlainTextEdit;
|
||||
m_logView->setReadOnly(true);
|
||||
layout->addWidget(m_logView, 1);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
ImportProgressPage::~ImportProgressPage() = default;
|
||||
|
||||
void ImportProgressPage::initializePage()
|
||||
{
|
||||
auto importWizard = qobject_cast<ImportWizard*>(wizard());
|
||||
Q_ASSERT(importWizard);
|
||||
Q_ASSERT(importWizard->database().isOpen());
|
||||
|
||||
const auto result = wizard()->property("result").value<ScanResult>();
|
||||
const auto timeFormat = wizard()->property("timeFormat").toString();
|
||||
|
||||
m_logView->clear();
|
||||
|
||||
m_thread = std::make_unique<ImportThread>(importWizard->database(), result, timeFormat, this);
|
||||
connect(m_thread.get(), &ImportThread::statusUpdate, this, &ImportProgressPage::statusUpdate);
|
||||
connect(m_thread.get(), &ImportThread::progressUpdate, this, &ImportProgressPage::progressUpdate);
|
||||
connect(m_thread.get(), &ImportThread::logMessage, this, &ImportProgressPage::logMessage);
|
||||
connect(m_thread.get(), &QThread::finished, this, &ImportProgressPage::finished);
|
||||
m_thread->start();
|
||||
|
||||
m_labelIcon->setMovie(&m_movieLoading);
|
||||
m_movieLoading.start();
|
||||
}
|
||||
|
||||
void ImportProgressPage::cleanupPage()
|
||||
{
|
||||
if (m_thread)
|
||||
{
|
||||
m_thread->requestInterruption();
|
||||
m_thread->wait();
|
||||
m_thread = nullptr;
|
||||
}
|
||||
m_movieLoading.stop();
|
||||
}
|
||||
|
||||
int ImportProgressPage::nextId() const
|
||||
{
|
||||
return int(ImportWizard::Pages::Conclusion);
|
||||
}
|
||||
|
||||
bool ImportProgressPage::isComplete() const
|
||||
{
|
||||
return m_thread == nullptr;
|
||||
}
|
||||
|
||||
void ImportProgressPage::statusUpdate(const QString &message)
|
||||
{
|
||||
m_labelStatus->setText(message);
|
||||
}
|
||||
|
||||
void ImportProgressPage::progressUpdate(qint64 finished, qint64 total)
|
||||
{
|
||||
while (total & 0xFFFFFFFF00000000)
|
||||
{
|
||||
finished = finished >> 8;
|
||||
total = total >> 8;
|
||||
}
|
||||
|
||||
m_progressBar->setMaximum(total);
|
||||
m_progressBar->setValue(finished);
|
||||
}
|
||||
|
||||
void ImportProgressPage::logMessage(const QString &message)
|
||||
{
|
||||
m_logView->appendHtml(QString("%0: %1<br/>").arg(QTime::currentTime().toString(), message));
|
||||
}
|
||||
|
||||
void ImportProgressPage::finished()
|
||||
{
|
||||
cleanupPage();
|
||||
emit completeChanged();
|
||||
|
||||
m_labelIcon->setPixmap(m_pixmapSucceeded);
|
||||
logMessage(tr("Finished."));
|
||||
}
|
46
wizard/importprogresspage.h
Normal file
46
wizard/importprogresspage.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWizardPage>
|
||||
#include <QPixmap>
|
||||
#include <QMovie>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class QLabel;
|
||||
class QProgressBar;
|
||||
class QPlainTextEdit;
|
||||
|
||||
class ImportThread;
|
||||
|
||||
class ImportProgressPage : public QWizardPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
const QPixmap m_pixmapSucceeded { ":/loganalyzer/succeeded.png" };
|
||||
const QPixmap m_pixmapFailed { ":/loganalyzer/failed.png" };
|
||||
QMovie m_movieLoading { ":/loganalyzer/loading.gif" };
|
||||
|
||||
public:
|
||||
explicit ImportProgressPage(QWidget *parent = nullptr);
|
||||
~ImportProgressPage() override;
|
||||
|
||||
void initializePage() override;
|
||||
void cleanupPage() override;
|
||||
|
||||
int nextId() const override;
|
||||
bool isComplete() const override;
|
||||
|
||||
private slots:
|
||||
void statusUpdate(const QString &message);
|
||||
void progressUpdate(qint64 finished, qint64 total);
|
||||
void logMessage(const QString &message);
|
||||
void finished();
|
||||
|
||||
private:
|
||||
QLabel *m_labelIcon;
|
||||
QLabel *m_labelStatus;
|
||||
QProgressBar *m_progressBar;
|
||||
QPlainTextEdit *m_logView;
|
||||
|
||||
std::unique_ptr<ImportThread> m_thread;
|
||||
};
|
117
wizard/importtypepage.cpp
Normal file
117
wizard/importtypepage.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
#include "importtypepage.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QRadioButton>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QToolButton>
|
||||
#include <QDir>
|
||||
#include <QMessageBox>
|
||||
#include <QSet>
|
||||
#include <QFileDialog>
|
||||
|
||||
#include "importwizard.h"
|
||||
#include "common.h"
|
||||
|
||||
ImportTypePage::ImportTypePage(QWidget *parent) :
|
||||
QWizardPage(parent)
|
||||
{
|
||||
setTitle(tr("Import type"));
|
||||
setSubTitle(tr("Please select which type of log files you would like to import."));
|
||||
|
||||
auto layout = new QVBoxLayout;
|
||||
|
||||
m_radioLocal = new QRadioButton(tr("Local: Typically found under /tmp/testfw_log/tests"));
|
||||
m_radioLocal->setChecked(true);
|
||||
layout->addWidget(m_radioLocal);
|
||||
|
||||
m_radioRemote = new QRadioButton(tr("Remote: Typically found under /log or /log2"));
|
||||
layout->addWidget(m_radioRemote);
|
||||
|
||||
layout->addStretch(1);
|
||||
|
||||
{
|
||||
auto hboxLayout = new QHBoxLayout;
|
||||
|
||||
m_lineEdit = new QLineEdit;
|
||||
hboxLayout->addWidget(m_lineEdit, 1);
|
||||
registerField("folder", m_lineEdit);
|
||||
|
||||
{
|
||||
auto toolButton = new QToolButton;
|
||||
toolButton->setText(tr("Select..."));
|
||||
connect(toolButton, &QAbstractButton::pressed, this, &ImportTypePage::selectFolder);
|
||||
hboxLayout->addWidget(toolButton);
|
||||
}
|
||||
|
||||
layout->addLayout(hboxLayout);
|
||||
}
|
||||
|
||||
layout->addStretch(1);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
int ImportTypePage::nextId() const
|
||||
{
|
||||
if (m_radioLocal->isChecked())
|
||||
return int(ImportWizard::Pages::LocalImport);
|
||||
if (m_radioRemote->isChecked())
|
||||
return int(ImportWizard::Pages::RemoteImportScan);
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
bool ImportTypePage::validatePage()
|
||||
{
|
||||
if (m_lineEdit->text().isEmpty())
|
||||
{
|
||||
QMessageBox::warning(this, tr("No logfolder defined!"), tr("No logfolder defined!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
QDir dir(m_lineEdit->text());
|
||||
if (!dir.exists())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Could not find logfolder!"), tr("Could not find logfolder!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_radioLocal->isChecked())
|
||||
{
|
||||
ScanResult result;
|
||||
auto &host = result["__dummyHost"];
|
||||
|
||||
for (const auto &fileInfo : dir.entryInfoList({ "*.log" }, QDir::Files))
|
||||
{
|
||||
if (fileInfo.baseName().endsWith("_console"))
|
||||
continue;
|
||||
|
||||
host[fileInfo.baseName()][QDate()] = {
|
||||
fileInfo.fileName(),
|
||||
fileInfo.absoluteFilePath(),
|
||||
fileInfo.size(),
|
||||
false
|
||||
};
|
||||
}
|
||||
|
||||
if (host.isEmpty())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Could not find any logs!"), tr("Could not find any logs!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
wizard()->setProperty("result", QVariant::fromValue(result));
|
||||
}
|
||||
|
||||
if (m_radioRemote->isChecked())
|
||||
wizard()->setProperty("folder", dir.absolutePath());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImportTypePage::selectFolder()
|
||||
{
|
||||
const auto path = QFileDialog::getExistingDirectory(this, tr("Select log folder"));
|
||||
if (!path.isEmpty())
|
||||
m_lineEdit->setText(path);
|
||||
}
|
27
wizard/importtypepage.h
Normal file
27
wizard/importtypepage.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWizardPage>
|
||||
|
||||
class QRadioButton;
|
||||
class QLineEdit;
|
||||
|
||||
class ImportTypePage : public QWizardPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ImportTypePage(QWidget *parent = nullptr);
|
||||
|
||||
int nextId() const override;
|
||||
|
||||
bool validatePage() override;
|
||||
|
||||
private slots:
|
||||
void selectFolder();
|
||||
|
||||
private:
|
||||
QRadioButton *m_radioLocal;
|
||||
QRadioButton *m_radioRemote;
|
||||
|
||||
QLineEdit *m_lineEdit;
|
||||
};
|
35
wizard/importwizard.cpp
Normal file
35
wizard/importwizard.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "importwizard.h"
|
||||
|
||||
#include "intropage.h"
|
||||
#include "databasepage.h"
|
||||
#include "tablespage.h"
|
||||
#include "importtypepage.h"
|
||||
#include "localimportpage.h"
|
||||
#include "remoteimportscanpage.h"
|
||||
#include "remoteimportoverviewpage.h"
|
||||
#include "importprogresspage.h"
|
||||
#include "conclusionpage.h"
|
||||
|
||||
ImportWizard::ImportWizard(QWidget *parent, Qt::WindowFlags flags) :
|
||||
QWizard(parent, flags)
|
||||
{
|
||||
setPage(int(Pages::Introduction), new IntroPage);
|
||||
setPage(int(Pages::Database), new DatabasePage);
|
||||
setPage(int(Pages::Tables), new TablesPage);
|
||||
setPage(int(Pages::ImportType), new ImportTypePage);
|
||||
setPage(int(Pages::LocalImport), new LocalImportPage);
|
||||
setPage(int(Pages::RemoteImportScan), new RemoteImportScanPage);
|
||||
setPage(int(Pages::RemoteImportOverview), new RemoteImportOverviewPage);
|
||||
setPage(int(Pages::ImportProgress), new ImportProgressPage);
|
||||
setPage(int(Pages::Conclusion), new ConclusionPage);
|
||||
}
|
||||
|
||||
QSqlDatabase &ImportWizard::database()
|
||||
{
|
||||
return m_database;
|
||||
}
|
||||
|
||||
const QSqlDatabase &ImportWizard::database() const
|
||||
{
|
||||
return m_database;
|
||||
}
|
20
wizard/importwizard.h
Normal file
20
wizard/importwizard.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWizard>
|
||||
#include <QSqlDatabase>
|
||||
|
||||
class ImportWizard : public QWizard
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum class Pages { Introduction, Database, Tables, ImportType, LocalImport, RemoteImportScan, RemoteImportOverview, ImportProgress, Conclusion };
|
||||
|
||||
ImportWizard(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
|
||||
|
||||
QSqlDatabase &database();
|
||||
const QSqlDatabase &database() const;
|
||||
|
||||
private:
|
||||
QSqlDatabase m_database;
|
||||
};
|
18
wizard/intropage.cpp
Normal file
18
wizard/intropage.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "intropage.h"
|
||||
#include "ui_intropage.h"
|
||||
|
||||
#include "importwizard.h"
|
||||
|
||||
IntroPage::IntroPage(QWidget *parent) :
|
||||
QWizardPage(parent),
|
||||
m_ui(std::make_unique<Ui::IntroPage>())
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
}
|
||||
|
||||
IntroPage::~IntroPage() = default;
|
||||
|
||||
int IntroPage::nextId() const
|
||||
{
|
||||
return int(ImportWizard::Pages::Database);
|
||||
}
|
21
wizard/intropage.h
Normal file
21
wizard/intropage.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWizardPage>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Ui { class IntroPage; }
|
||||
|
||||
class IntroPage : public QWizardPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit IntroPage(QWidget *parent = nullptr);
|
||||
~IntroPage() override;
|
||||
|
||||
int nextId() const override;
|
||||
|
||||
private:
|
||||
const std::unique_ptr<Ui::IntroPage> m_ui;
|
||||
};
|
34
wizard/intropage.ui
Normal file
34
wizard/intropage.ui
Normal file
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>IntroPage</class>
|
||||
<widget class="QWizardPage" name="IntroPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>WizardPage</string>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Introduction</string>
|
||||
</property>
|
||||
<property name="subTitle">
|
||||
<string>TODO...</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>TODO: long introduction...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
161
wizard/localimportpage.cpp
Normal file
161
wizard/localimportpage.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
#include "localimportpage.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QFormLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QHostInfo>
|
||||
#include <QDateEdit>
|
||||
#include <QComboBox>
|
||||
#include <QListView>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QStringBuilder>
|
||||
|
||||
#include "importwizard.h"
|
||||
|
||||
LocalImportPage::LocalImportPage(QWidget *parent) :
|
||||
QWizardPage(parent)
|
||||
{
|
||||
setTitle(tr("Local Import"));
|
||||
setSubTitle(tr("TODO..."));
|
||||
setCommitPage(true);
|
||||
|
||||
connect(&m_model, &ChecklistModel::dataChanged, this, &LocalImportPage::updateSummary);
|
||||
|
||||
auto layout = new QVBoxLayout;
|
||||
|
||||
{
|
||||
auto hboxLayout = new QHBoxLayout;
|
||||
|
||||
{
|
||||
auto formLayout = new QFormLayout;
|
||||
|
||||
m_lineEditHost = new QLineEdit(QHostInfo::localHostName());
|
||||
formLayout->addRow(tr("Host:"), m_lineEditHost);
|
||||
|
||||
m_dateEdit = new QDateEdit(QDate::currentDate());
|
||||
formLayout->addRow(tr("Date:"), m_dateEdit);
|
||||
|
||||
m_comboBox = new QComboBox;
|
||||
m_comboBox->addItem(tr("Without milliseconds"), "HH:mm:ss");
|
||||
m_comboBox->addItem(tr("With milliseconds"), "HH:mm:ss.zzz");
|
||||
formLayout->addRow(tr("Timestamp:"), m_comboBox);
|
||||
|
||||
hboxLayout->addLayout(formLayout);
|
||||
}
|
||||
|
||||
{
|
||||
auto view = new QListView;
|
||||
view->setModel(&m_model);
|
||||
hboxLayout->addWidget(view, 1);
|
||||
}
|
||||
|
||||
layout->addLayout(hboxLayout, 1);
|
||||
}
|
||||
|
||||
m_labelSummary = new QLabel;
|
||||
layout->addWidget(m_labelSummary);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
int LocalImportPage::nextId() const
|
||||
{
|
||||
return int(ImportWizard::Pages::ImportProgress);
|
||||
}
|
||||
|
||||
void LocalImportPage::initializePage()
|
||||
{
|
||||
m_result = wizard()->property("result").value<ScanResult>();
|
||||
|
||||
Q_ASSERT(m_result.count() == 1);
|
||||
|
||||
{
|
||||
auto processes = m_result.values().first().keys();
|
||||
processes.sort();
|
||||
m_model.setItems(processes);
|
||||
}
|
||||
|
||||
updateSummary();
|
||||
}
|
||||
|
||||
bool LocalImportPage::validatePage()
|
||||
{
|
||||
auto result = filterResult(m_result);
|
||||
|
||||
if (scanResultEmpty(result))
|
||||
{
|
||||
QMessageBox::warning(this, tr("No files to import!"), tr("No files to import!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
Q_ASSERT(result.count() == 1);
|
||||
|
||||
auto host = result.values().first();
|
||||
for (auto iter = host.begin(); iter != host.end(); iter++)
|
||||
{
|
||||
auto &dates = iter.value();
|
||||
Q_ASSERT(dates.count() == 1);
|
||||
|
||||
const auto logfile = dates.values().first();
|
||||
dates.clear();
|
||||
dates.insert(m_dateEdit->date(), logfile);
|
||||
}
|
||||
|
||||
result.clear();
|
||||
result.insert(m_lineEditHost->text(), host);
|
||||
|
||||
wizard()->setProperty("result", QVariant::fromValue(result));
|
||||
wizard()->setProperty("timeFormat", m_comboBox->currentData().toString());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LocalImportPage::updateSummary()
|
||||
{
|
||||
if (m_result.isEmpty())
|
||||
return;
|
||||
|
||||
const auto result = filterResult(m_result);
|
||||
|
||||
int logFiles { 0 };
|
||||
qint64 totalSize { 0 };
|
||||
|
||||
for (auto hostsIter = result.constBegin(); hostsIter != result.constEnd(); hostsIter++)
|
||||
for (auto processesIter = hostsIter.value().constBegin(); processesIter != hostsIter.value().constEnd(); processesIter++)
|
||||
for (auto datesIter = processesIter.value().constBegin(); datesIter != processesIter.value().constEnd(); datesIter++)
|
||||
{
|
||||
logFiles++;
|
||||
totalSize += datesIter.value().filesize;
|
||||
}
|
||||
|
||||
QString sizeStr;
|
||||
for (const QString prefix : { "K", "M", "G", "T" })
|
||||
{
|
||||
if (totalSize > 1024)
|
||||
{
|
||||
totalSize /= 1024;
|
||||
sizeStr = QString::number(totalSize) % prefix;
|
||||
}
|
||||
}
|
||||
|
||||
m_labelSummary->setText(tr("Filters match %0 files (%1B)").arg(logFiles).arg(sizeStr));
|
||||
}
|
||||
|
||||
ScanResult LocalImportPage::filterResult(ScanResult result) const
|
||||
{
|
||||
const auto processes = m_model.enabledItems().toSet();
|
||||
|
||||
for (auto hostsIter = result.begin(); hostsIter != result.end(); hostsIter++)
|
||||
{
|
||||
for (auto processesIter = hostsIter.value().begin(); processesIter != hostsIter.value().end(); )
|
||||
{
|
||||
if (processes.contains(processesIter.key()))
|
||||
processesIter++;
|
||||
else
|
||||
processesIter = hostsIter.value().erase(processesIter);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
39
wizard/localimportpage.h
Normal file
39
wizard/localimportpage.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWizardPage>
|
||||
|
||||
#include "models/checklistmodel.h"
|
||||
#include "common.h"
|
||||
|
||||
class QLineEdit;
|
||||
class QDateEdit;
|
||||
class QComboBox;
|
||||
class QLabel;
|
||||
|
||||
class LocalImportPage : public QWizardPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LocalImportPage(QWidget *parent = nullptr);
|
||||
|
||||
int nextId() const override;
|
||||
|
||||
void initializePage() override;
|
||||
bool validatePage() override;
|
||||
|
||||
private slots:
|
||||
void updateSummary();
|
||||
|
||||
private:
|
||||
ScanResult filterResult(ScanResult result) const;
|
||||
|
||||
ScanResult m_result;
|
||||
|
||||
ChecklistModel m_model;
|
||||
|
||||
QLineEdit *m_lineEditHost;
|
||||
QDateEdit *m_dateEdit;
|
||||
QComboBox *m_comboBox;
|
||||
QLabel *m_labelSummary;
|
||||
};
|
248
wizard/remoteimportoverviewpage.cpp
Normal file
248
wizard/remoteimportoverviewpage.cpp
Normal file
@@ -0,0 +1,248 @@
|
||||
#include "remoteimportoverviewpage.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QDateEdit>
|
||||
#include <QComboBox>
|
||||
#include <QListView>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QVariant>
|
||||
#include <QRegularExpression>
|
||||
#include <QSet>
|
||||
#include <QDate>
|
||||
#include <QStringBuilder>
|
||||
|
||||
#include "importwizard.h"
|
||||
#include "common.h"
|
||||
|
||||
RemoteImportOverviewPage::RemoteImportOverviewPage(QWidget *parent) :
|
||||
QWizardPage(parent)
|
||||
{
|
||||
setTitle(tr("Remote Import Overview"));
|
||||
setSubTitle(tr("TODO...."));
|
||||
setCommitPage(true);
|
||||
|
||||
connect(&m_modelHosts, &ChecklistModel::dataChanged, this, &RemoteImportOverviewPage::updateSummary);
|
||||
connect(&m_modelProcesses, &ChecklistModel::dataChanged, this, &RemoteImportOverviewPage::updateSummary);
|
||||
|
||||
auto layout = new QVBoxLayout;
|
||||
|
||||
{
|
||||
auto groupBox = new QGroupBox(tr("Date-Range:"));
|
||||
|
||||
auto hboxLayout = new QHBoxLayout;
|
||||
|
||||
{
|
||||
auto label = new QLabel(tr("From:"));
|
||||
hboxLayout->addWidget(label);
|
||||
}
|
||||
|
||||
m_dateEditFrom = new QDateEdit;
|
||||
connect(m_dateEditFrom, &QDateTimeEdit::dateChanged, this, &RemoteImportOverviewPage::updateSummary);
|
||||
hboxLayout->addWidget(m_dateEditFrom);
|
||||
|
||||
hboxLayout->addStretch(1);
|
||||
|
||||
{
|
||||
auto label = new QLabel(tr("To:"));
|
||||
hboxLayout->addWidget(label);
|
||||
}
|
||||
|
||||
m_dateEditTo = new QDateEdit;
|
||||
connect(m_dateEditTo, &QDateTimeEdit::dateChanged, this, &RemoteImportOverviewPage::updateSummary);
|
||||
hboxLayout->addWidget(m_dateEditTo);
|
||||
|
||||
hboxLayout->addStretch(1);
|
||||
|
||||
{
|
||||
auto label = new QLabel(tr("Timestamp:"));
|
||||
hboxLayout->addWidget(label);
|
||||
}
|
||||
|
||||
m_comboBox = new QComboBox;
|
||||
m_comboBox->addItem(tr("Without milliseconds"), "HH:mm:ss");
|
||||
m_comboBox->addItem(tr("With milliseconds"), "HH:mm:ss.zzz");
|
||||
hboxLayout->addWidget(m_comboBox);
|
||||
|
||||
groupBox->setLayout(hboxLayout);
|
||||
|
||||
layout->addWidget(groupBox);
|
||||
}
|
||||
|
||||
{
|
||||
auto hboxLayout = new QHBoxLayout;
|
||||
|
||||
{
|
||||
auto vboxLayout = new QVBoxLayout;
|
||||
|
||||
{
|
||||
auto label = new QLabel(tr("Hosts:"));
|
||||
vboxLayout->addWidget(label);
|
||||
}
|
||||
|
||||
{
|
||||
auto view = new QListView;
|
||||
view->setModel(&m_modelHosts);
|
||||
vboxLayout->addWidget(view, 1);
|
||||
}
|
||||
|
||||
hboxLayout->addLayout(vboxLayout);
|
||||
}
|
||||
|
||||
{
|
||||
auto vboxLayout = new QVBoxLayout;
|
||||
|
||||
{
|
||||
auto label = new QLabel(tr("Processes:"));
|
||||
vboxLayout->addWidget(label);
|
||||
}
|
||||
|
||||
{
|
||||
auto view = new QListView;
|
||||
view->setModel(&m_modelProcesses);
|
||||
vboxLayout->addWidget(view, 1);
|
||||
}
|
||||
|
||||
hboxLayout->addLayout(vboxLayout);
|
||||
}
|
||||
|
||||
layout->addLayout(hboxLayout, 1);
|
||||
}
|
||||
|
||||
m_labelSummary = new QLabel;
|
||||
layout->addWidget(m_labelSummary);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void RemoteImportOverviewPage::initializePage()
|
||||
{
|
||||
m_result = wizard()->property("result").value<ScanResult>();
|
||||
|
||||
QDate minDate, maxDate;
|
||||
QSet<QString> processes;
|
||||
|
||||
for (auto hostsIter = m_result.constBegin(); hostsIter != m_result.constEnd(); hostsIter++)
|
||||
for (auto processesIter = hostsIter.value().constBegin(); processesIter != hostsIter.value().constEnd(); processesIter++)
|
||||
{
|
||||
processes.insert(processesIter.key());
|
||||
|
||||
for (auto datesIter = processesIter.value().constBegin(); datesIter != processesIter.value().constEnd(); datesIter++)
|
||||
{
|
||||
if (minDate.isNull() || datesIter.key() < minDate)
|
||||
minDate = datesIter.key();
|
||||
if (maxDate.isNull() || datesIter.key() > maxDate)
|
||||
maxDate = datesIter.key();
|
||||
}
|
||||
}
|
||||
|
||||
m_dateEditFrom->setDate(minDate);
|
||||
m_dateEditTo->setDate(maxDate);
|
||||
|
||||
{
|
||||
auto hosts = m_result.keys();
|
||||
hosts.sort();
|
||||
m_modelHosts.setItems(hosts);
|
||||
}
|
||||
|
||||
{
|
||||
auto processesList = processes.toList();
|
||||
processesList.sort();
|
||||
m_modelProcesses.setItems(processesList);
|
||||
}
|
||||
|
||||
updateSummary();
|
||||
}
|
||||
|
||||
int RemoteImportOverviewPage::nextId() const
|
||||
{
|
||||
return int(ImportWizard::Pages::ImportProgress);
|
||||
}
|
||||
|
||||
bool RemoteImportOverviewPage::validatePage()
|
||||
{
|
||||
const auto result = filterResult(m_result);
|
||||
|
||||
if (scanResultEmpty(result))
|
||||
{
|
||||
QMessageBox::warning(this, tr("No files to import!"), tr("No files to import!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
wizard()->setProperty("result", QVariant::fromValue(result));
|
||||
wizard()->setProperty("timeFormat", m_comboBox->currentData().toString());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RemoteImportOverviewPage::updateSummary()
|
||||
{
|
||||
if (m_result.isEmpty())
|
||||
return;
|
||||
|
||||
const auto result = filterResult(m_result);
|
||||
|
||||
int logFiles { 0 };
|
||||
qint64 totalSize { 0 };
|
||||
|
||||
for (auto hostsIter = result.constBegin(); hostsIter != result.constEnd(); hostsIter++)
|
||||
for (auto processesIter = hostsIter.value().constBegin(); processesIter != hostsIter.value().constEnd(); processesIter++)
|
||||
for (auto datesIter = processesIter.value().constBegin(); datesIter != processesIter.value().constEnd(); datesIter++)
|
||||
{
|
||||
logFiles++;
|
||||
totalSize += datesIter.value().filesize;
|
||||
}
|
||||
|
||||
QString sizeStr;
|
||||
for (const QString prefix : { "K", "M", "G", "T" })
|
||||
{
|
||||
if (totalSize > 1024)
|
||||
{
|
||||
totalSize /= 1024;
|
||||
sizeStr = QString::number(totalSize) % prefix;
|
||||
}
|
||||
}
|
||||
|
||||
m_labelSummary->setText(tr("Filters match %0 files (%1B)").arg(logFiles).arg(sizeStr));
|
||||
}
|
||||
|
||||
ScanResult RemoteImportOverviewPage::filterResult(ScanResult result) const
|
||||
{
|
||||
const auto hosts = m_modelHosts.enabledItems().toSet();
|
||||
const auto processes = m_modelProcesses.enabledItems().toSet();
|
||||
|
||||
for (auto hostsIter = result.begin(); hostsIter != result.end(); )
|
||||
{
|
||||
if (hosts.contains(hostsIter.key()))
|
||||
{
|
||||
for (auto processesIter = hostsIter.value().begin(); processesIter != hostsIter.value().end(); )
|
||||
{
|
||||
if (processes.contains(processesIter.key()))
|
||||
{
|
||||
for (auto datesIter = processesIter.value().begin(); datesIter != processesIter.value().end(); )
|
||||
{
|
||||
if (datesIter.key() >= m_dateEditFrom->date() && datesIter.key() <= m_dateEditTo->date())
|
||||
datesIter++;
|
||||
else
|
||||
datesIter = processesIter.value().erase(datesIter);
|
||||
}
|
||||
|
||||
processesIter++;
|
||||
}
|
||||
else
|
||||
processesIter = hostsIter.value().erase(processesIter);
|
||||
}
|
||||
|
||||
hostsIter++;
|
||||
}
|
||||
else
|
||||
hostsIter = result.erase(hostsIter);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
40
wizard/remoteimportoverviewpage.h
Normal file
40
wizard/remoteimportoverviewpage.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWizardPage>
|
||||
|
||||
#include "models/checklistmodel.h"
|
||||
#include "common.h"
|
||||
|
||||
class QDateEdit;
|
||||
class QComboBox;
|
||||
class QLabel;
|
||||
|
||||
class RemoteImportOverviewPage : public QWizardPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RemoteImportOverviewPage(QWidget *parent = nullptr);
|
||||
|
||||
void initializePage() override;
|
||||
|
||||
int nextId() const override;
|
||||
|
||||
bool validatePage() override;
|
||||
|
||||
private slots:
|
||||
void updateSummary();
|
||||
|
||||
private:
|
||||
ScanResult filterResult(ScanResult result) const;
|
||||
|
||||
QDateEdit *m_dateEditFrom;
|
||||
QDateEdit *m_dateEditTo;
|
||||
QComboBox *m_comboBox;
|
||||
QLabel *m_labelSummary;
|
||||
|
||||
ChecklistModel m_modelHosts;
|
||||
ChecklistModel m_modelProcesses;
|
||||
|
||||
ScanResult m_result;
|
||||
};
|
111
wizard/remoteimportscanpage.cpp
Normal file
111
wizard/remoteimportscanpage.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#include "remoteimportscanpage.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
#include "importwizard.h"
|
||||
#include "threads/remotescannerthread.h"
|
||||
|
||||
RemoteImportScanPage::RemoteImportScanPage(QWidget *parent) :
|
||||
QWizardPage(parent)
|
||||
{
|
||||
setTitle(tr("Remote Import Scan"));
|
||||
setSubTitle(tr("TODO..."));
|
||||
|
||||
auto layout = new QVBoxLayout;
|
||||
|
||||
{
|
||||
auto hboxLayout = new QHBoxLayout;
|
||||
|
||||
m_labelAnimation = new QLabel;
|
||||
hboxLayout->addWidget(m_labelAnimation);
|
||||
|
||||
m_labelStatus = new QLabel;
|
||||
hboxLayout->addWidget(m_labelStatus, 1);
|
||||
|
||||
layout->addLayout(hboxLayout);
|
||||
}
|
||||
|
||||
m_logView = new QPlainTextEdit;
|
||||
m_logView->setReadOnly(true);
|
||||
|
||||
layout->addWidget(m_logView, 1);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
RemoteImportScanPage::~RemoteImportScanPage() = default;
|
||||
|
||||
int RemoteImportScanPage::nextId() const
|
||||
{
|
||||
return int(ImportWizard::Pages::RemoteImportOverview);
|
||||
}
|
||||
|
||||
void RemoteImportScanPage::initializePage()
|
||||
{
|
||||
wizard()->setProperty("result", QVariant::fromValue(ScanResult()));
|
||||
|
||||
m_labelAnimation->setMovie(&m_movieLoading);
|
||||
m_movieLoading.start();
|
||||
|
||||
m_logView->clear();
|
||||
|
||||
m_thread = std::make_unique<RemoteScannerThread>(field("folder").toString(), this);
|
||||
|
||||
connect(m_thread.get(), &RemoteScannerThread::progressUpdate, this, &RemoteImportScanPage::progressUpdate);
|
||||
connect(m_thread.get(), &RemoteScannerThread::logMessage, this, &RemoteImportScanPage::logMessage);
|
||||
connect(m_thread.get(), &RemoteScannerThread::finished, this, &RemoteImportScanPage::finished);
|
||||
|
||||
m_thread->start();
|
||||
}
|
||||
|
||||
void RemoteImportScanPage::cleanupPage()
|
||||
{
|
||||
if (m_thread)
|
||||
{
|
||||
m_thread->requestInterruption();
|
||||
m_thread->wait();
|
||||
m_thread = nullptr;
|
||||
}
|
||||
m_movieLoading.stop();
|
||||
}
|
||||
|
||||
bool RemoteImportScanPage::isComplete() const
|
||||
{
|
||||
return m_thread == nullptr && !scanResultEmpty(wizard()->property("result").value<ScanResult>());
|
||||
}
|
||||
|
||||
void RemoteImportScanPage::progressUpdate(int totalFiles, int skippedFiles)
|
||||
{
|
||||
m_labelStatus->setText(tr("%0 files scanned... (%1 files skipped)").arg(totalFiles).arg(skippedFiles));
|
||||
}
|
||||
|
||||
void RemoteImportScanPage::logMessage(const QString &message)
|
||||
{
|
||||
m_logView->appendHtml(QString("%0: %1<br/>").arg(QTime::currentTime().toString(), message));
|
||||
}
|
||||
|
||||
void RemoteImportScanPage::finished(const ScanResult &result)
|
||||
{
|
||||
m_labelAnimation->setMovie(nullptr);
|
||||
|
||||
const auto success = !scanResultEmpty(result);
|
||||
if (success)
|
||||
{
|
||||
logMessage(tr("Finished"));
|
||||
m_labelAnimation->setPixmap(m_pixmapSucceeded);
|
||||
|
||||
wizard()->setProperty("result", QVariant::fromValue(result));
|
||||
}
|
||||
else
|
||||
{
|
||||
logMessage(tr("Scan failed."));
|
||||
m_labelAnimation->setPixmap(m_pixmapFailed);
|
||||
}
|
||||
|
||||
cleanupPage();
|
||||
if (success)
|
||||
emit completeChanged();
|
||||
}
|
45
wizard/remoteimportscanpage.h
Normal file
45
wizard/remoteimportscanpage.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWizardPage>
|
||||
#include <QPixmap>
|
||||
#include <QMovie>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
class QLabel;
|
||||
class QPlainTextEdit;
|
||||
|
||||
class RemoteScannerThread;
|
||||
|
||||
class RemoteImportScanPage : public QWizardPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
const QPixmap m_pixmapSucceeded { ":/loganalyzer/succeeded.png" };
|
||||
const QPixmap m_pixmapFailed { ":/loganalyzer/failed.png" };
|
||||
QMovie m_movieLoading { ":/loganalyzer/loading.gif" };
|
||||
|
||||
public:
|
||||
explicit RemoteImportScanPage(QWidget *parent = nullptr);
|
||||
~RemoteImportScanPage() override;
|
||||
|
||||
int nextId() const override;
|
||||
|
||||
void initializePage() override;
|
||||
void cleanupPage() override;
|
||||
bool isComplete() const override;
|
||||
|
||||
private slots:
|
||||
void progressUpdate(int totalFiles, int skippedFiles);
|
||||
void logMessage(const QString &message);
|
||||
void finished(const ScanResult &result);
|
||||
|
||||
private:
|
||||
std::unique_ptr<RemoteScannerThread> m_thread;
|
||||
|
||||
QLabel *m_labelAnimation;
|
||||
QLabel *m_labelStatus;
|
||||
QPlainTextEdit *m_logView;
|
||||
};
|
89
wizard/tablespage.cpp
Normal file
89
wizard/tablespage.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#include "tablespage.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
|
||||
#include "importwizard.h"
|
||||
#include "threads/tablecreatorthread.h"
|
||||
|
||||
TablesPage::TablesPage(QWidget *parent) :
|
||||
QWizardPage(parent)
|
||||
{
|
||||
setTitle(tr("Tables"));
|
||||
setSubTitle(tr("TODO..."));
|
||||
|
||||
auto layout = new QGridLayout;
|
||||
|
||||
m_statusLabels.resize(TableCreatorThread::tables().size());
|
||||
|
||||
int index { 0 };
|
||||
for (const QString &tableName : TableCreatorThread::tables())
|
||||
{
|
||||
m_statusLabels[index] = new QLabel;
|
||||
layout->addWidget(m_statusLabels[index], index, 0);
|
||||
|
||||
auto label = new QLabel(tr("Create table %0").arg(tableName));
|
||||
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
layout->addWidget(label, index++, 1);
|
||||
}
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
TablesPage::~TablesPage() = default;
|
||||
|
||||
int TablesPage::nextId() const
|
||||
{
|
||||
return int(ImportWizard::Pages::ImportType);
|
||||
}
|
||||
|
||||
void TablesPage::initializePage()
|
||||
{
|
||||
auto importWizard = qobject_cast<ImportWizard*>(wizard());
|
||||
Q_ASSERT(importWizard);
|
||||
Q_ASSERT(importWizard->database().isOpen());
|
||||
|
||||
for (auto label : m_statusLabels)
|
||||
{
|
||||
label->setMovie(nullptr);
|
||||
label->setPixmap({});
|
||||
}
|
||||
|
||||
m_thread = std::make_unique<TableCreatorThread>(importWizard->database(), this);
|
||||
connect(m_thread.get(), &TableCreatorThread::someSignal, this, &TablesPage::someSlot);
|
||||
|
||||
m_thread->start();
|
||||
m_statusLabels[0]->setMovie(&m_movieLoading);
|
||||
m_movieLoading.start();
|
||||
}
|
||||
|
||||
void TablesPage::cleanupPage()
|
||||
{
|
||||
if (m_thread)
|
||||
{
|
||||
m_thread->requestInterruption();
|
||||
m_thread->wait();
|
||||
m_thread = nullptr;
|
||||
}
|
||||
m_movieLoading.stop();
|
||||
}
|
||||
|
||||
bool TablesPage::isComplete() const
|
||||
{
|
||||
return m_thread == nullptr;
|
||||
}
|
||||
|
||||
void TablesPage::someSlot(int index)
|
||||
{
|
||||
Q_ASSERT(index < m_statusLabels.size());
|
||||
|
||||
m_statusLabels[index]->setMovie(nullptr);
|
||||
m_statusLabels[index]->setPixmap(m_pixmapSucceeded);
|
||||
if (index < m_statusLabels.size() - 1)
|
||||
m_statusLabels[index+1]->setMovie(&m_movieLoading);
|
||||
else
|
||||
{
|
||||
cleanupPage();
|
||||
emit completeChanged();
|
||||
}
|
||||
}
|
40
wizard/tablespage.h
Normal file
40
wizard/tablespage.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWizardPage>
|
||||
#include <QPixmap>
|
||||
#include <QMovie>
|
||||
#include <QVector>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class QLabel;
|
||||
class QSqlDatabase;
|
||||
|
||||
class TableCreatorThread;
|
||||
|
||||
class TablesPage : public QWizardPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
const QPixmap m_pixmapSucceeded { ":/loganalyzer/succeeded.png" };
|
||||
const QPixmap m_pixmapFailed { ":/loganalyzer/failed.png" };
|
||||
QMovie m_movieLoading { ":/loganalyzer/loading.gif" };
|
||||
|
||||
public:
|
||||
explicit TablesPage(QWidget *parent = nullptr);
|
||||
~TablesPage() override;
|
||||
|
||||
int nextId() const override;
|
||||
|
||||
void initializePage() override;
|
||||
void cleanupPage() override;
|
||||
bool isComplete() const override;
|
||||
|
||||
private slots:
|
||||
void someSlot(int index);
|
||||
|
||||
private:
|
||||
QVector<QLabel*> m_statusLabels;
|
||||
|
||||
std::unique_ptr<TableCreatorThread> m_thread;
|
||||
};
|
Reference in New Issue
Block a user