Skip to content
Draft

Qxevent #1069

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6244272
Add libshv dependency to enable QxEvent service implementation
fvacek Nov 3, 2025
dce1969
Rename QxClientService to QxEventService
fvacek Nov 8, 2025
2b49c7c
Fix DesktopUtils::moveRectToVisibleDesktopScreen
fvacek Nov 9, 2025
4b1f96f
Update libshv
fvacek Nov 9, 2025
317424d
Add QX event SHV nodes
fvacek Nov 9, 2025
8972a3e
Remove unused shv odes
fvacek Nov 9, 2025
f0d2a69
QX SQL API node implemented
fvacek Nov 9, 2025
9e325a7
QxSQL API in QE
fvacek Nov 10, 2025
ee2002f
Do not reload whole row on qxSqlApi update recchng
fvacek Nov 12, 2025
46e5ff0
Recchng is emitted on SqlTableModel::postRow
fvacek Nov 17, 2025
3581d8a
Fix CardReader model id column name
fvacek Nov 23, 2025
e780bf8
Change default SHV mount point
fvacek Nov 30, 2025
7904e27
Enable QF_WITH_LIBSHV option in CMake action
fvacek Dec 19, 2025
c91655c
Make service status text expandable
fvacek Dec 23, 2025
603522b
Rebased on multi-course
fvacek Jan 30, 2026
d542aaa
Update CI linter ubuntu version
fvacek Feb 2, 2026
74aa2d0
Fix QxService login procedure
fvacek Feb 2, 2026
e338eed
Fix CI linter
fvacek Feb 2, 2026
60c37b4
Rebased to origin/main
fvacek Apr 8, 2026
27fba46
Remove duplicate include
fvacek Apr 8, 2026
a3a1a13
Fix clang-tidy warnings
fvacek Apr 9, 2026
a6d3fba
Add workaround for clazy in action.yml
fvacek Apr 9, 2026
833dc77
fix: add missing -DQF_WITH_LIBSHV=ON for macOS
lukaskett Apr 21, 2026
56d816b
Merge remote-tracking branch 'origin/main' into qxevent
fvacek Jun 1, 2026
98f57f2
Update libshv
fvacek Jun 1, 2026
b0f2e39
Fix broken build
fvacek Jun 1, 2026
c6072f3
Fix linter warnings
fvacek Jun 1, 2026
0d20550
Fix broken build
fvacek Jun 1, 2026
efe865e
SqlApi rewrite
fvacek Jun 1, 2026
2b21644
Move SqlApi to EventPlugin
fvacek Jun 3, 2026
ec77db0
Move QxSqlApi from qf::gui::framework::Application to SqlApi class
fvacek Jun 5, 2026
1dac941
Change qxchanges table structure, QE 3.6.0
fvacek Jun 8, 2026
947b1a3
Merge remote-tracking branch 'origin/main' into qxevent
fvacek Jun 8, 2026
30f6b3a
Fix ofeed client compiler warnings
fvacek Jun 8, 2026
03ab4d6
Fix create SQL scripts
fvacek Jun 9, 2026
3fa9d28
Add LateEntry support and update qxchanges schema
fvacek Jun 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Checks:
-cppcoreguidelines-pro-type-union-access,
-cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-special-member-functions,
-cppcoreguidelines-use-enum-class,
-hicpp-avoid-c-arrays,
-hicpp-avoid-goto,
-hicpp-braces-around-statements,
Expand Down
1 change: 1 addition & 0 deletions .github/actions/cmake/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ runs:
-B '${{github.workspace}}/build' \
-DCMAKE_BUILD_TYPE=Release \
-DQF_BUILD_QML_PLUGINS=ON \
-DQF_WITH_LIBSHV=ON \
-DBUILD_TESTING=OFF \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DUSE_QT6=${{ inputs.use_qt6 }} \
Expand Down
9 changes: 8 additions & 1 deletion .github/actions/run-linter/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,18 @@ runs:
modules: qtserialport qtmultimedia
additional_cmake_args: -DCMAKE_GLOBAL_AUTOGEN_TARGET=ON -DCMAKE_AUTOGEN_ORIGIN_DEPENDS=OFF

# https://bugs.launchpad.net/ubuntu/+source/clazy/+bug/2086665/comments/4
- name: Workaround for clazy
shell: bash
run: |
sudo apt-get remove gcc-14
sudo apt-get autoremove

# workaround for clang-tidy false positive
- uses: mjp41/workaround8649@c8550b715ccdc17f89c8d5c28d7a48eeff9c94a8
if: runner.os == 'Linux'
with:
os: ubuntu-latest
os: ubuntu-24.04

- name: Build autogenerated stuff
shell: bash
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ on:

jobs:
clang-tidy:
name: clang-tidy / Ubuntu 22.04
runs-on: ubuntu-22.04
name: clang-tidy / Ubuntu 24.04
runs-on: ubuntu-24.04
env:
CC: clang
CXX: clang++
Expand All @@ -26,8 +26,8 @@ jobs:
exclude_files_pattern: ^quickevent/app/quickevent/plugins/Receipts/src/thirdparty/qrcodegen\.cpp$

clazy:
name: clazy / Ubuntu 22.04
runs-on: ubuntu-22.04
name: clazy / Ubuntu 24.04
runs-on: ubuntu-24.04
env:
CC: clang
CXX: clang++
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ jobs:
-B '${{github.workspace}}/build' \
-DCMAKE_BUILD_TYPE=Release \
-DQF_BUILD_QML_PLUGINS=ON \
-DQF_WITH_LIBSHV=ON \
-DBUILD_TESTING=OFF \
-DUSE_QT6=ON \
-DCOMMIT_SHA=${{ env.COMMIT_SHA }} \
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "3rdparty/necrolog"]
path = 3rdparty/necrolog
url = https://github.com/fvacek/necrolog.git
[submodule "3rdparty/libshv"]
path = 3rdparty/libshv
url = https://github.com/silicon-heaven/libshv.git
1 change: 1 addition & 0 deletions 3rdparty/libshv
Submodule libshv added at b1c372
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
cmake_minimum_required(VERSION 3.18.4)

set(QF_BUILD_QML_PLUGINS ON CACHE BOOL "Build with QML Plugins support")
set(QF_WITH_LIBSHV OFF CACHE BOOL "Build with libshv")

project(quickbox LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 20)
Expand Down Expand Up @@ -34,6 +35,9 @@ endif (WIN32)
if (NOT TARGET libnecrolog)
add_subdirectory(3rdparty/necrolog)
endif()
if (QF_WITH_LIBSHV)
add_subdirectory(3rdparty/libshv)
endif()

set(USE_QT6 ON)
find_package(Qt6 REQUIRED COMPONENTS Core Widgets Gui Sql Qml Xml LinguistTools PrintSupport Svg SerialPort Multimedia Network)
Expand Down
1 change: 1 addition & 0 deletions libqf/libqfcore/src/sql/qxrecchng.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ QString QxRecChng::recopToString(RecOp op)
case RecOp::Update: return "Update";
case RecOp::Delete: return "Delete";
}
return QString();
}

}
183 changes: 156 additions & 27 deletions libqf/libqfcore/src/sql/qxsql.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "qxsql.h"

#include <qf/core/exception.h>
#include <qf/core/log.h>
#include <qf/core/sql/query.h>

#include <QSqlQuery>
#include <QSqlError>
Expand All @@ -11,6 +13,49 @@ namespace qf::core::sql {
//================================================================
// QxSqlApi
//================================================================
namespace {

class Transaction
{
public:
Transaction(QSqlDatabase db) : m_db(db) {
if (!m_db.transaction()) {
qfWarning() << "BEGIN transaction error:" << m_db.lastError().text();
throw std::runtime_error("BEGIN transaction error");
}
}
~Transaction() {
if (m_inTransaction) {
m_db.rollback();
}
}
void commit() {
if (!m_db.commit()) {
qfWarning() << "COMMIT transaction error:" << m_db.lastError().text();
throw std::runtime_error("COMMIT transaction error");
}
m_inTransaction = false;
}
private:
QSqlDatabase m_db;
bool m_inTransaction = true;
};
}
void QxSqlApi::transaction(const QString &query, const QVariantList &params, QSqlDatabase db)
{
Transaction transaction(db);
qf::core::sql::Query q(db);
q.prepare(query, qf::core::Exception::Throw);
for (const auto &param : params) {
auto m = param.toMap();
for (const auto &[k, v] : m.asKeyValueRange()) {
q.bindValue(':' + k, v);
}
q.exec(qf::core::Exception::Throw);
}
transaction.commit();
}

qint64 QxSqlApi::createRecord(const QString &table, const Record &record, const QString &issuer)
{
Q_UNUSED(issuer)
Expand Down Expand Up @@ -63,7 +108,7 @@ bool QxSqlApi::updateRecord(const QString &table, qint64 id, const Record &recor
.arg(table, keyVals.join(", "), QString::number(id));

ExecResult res = exec(qs, record);
return res.rowsAffected == 1;
return res.numRowsAffected == 1;
}

bool QxSqlApi::deleteRecord(const QString &table, qint64 id, const QString &issuer)
Expand All @@ -72,7 +117,7 @@ bool QxSqlApi::deleteRecord(const QString &table, qint64 id, const QString &issu
QString qs = QString("DELETE FROM %1 WHERE id = %2").arg(table, QString::number(id));

ExecResult res = exec(qs, {});
return res.rowsAffected == 1;
return res.numRowsAffected == 1;
}

QList<Record> QxSqlApi::listOneOrMoreRecords(const QString &table, const std::optional<QStringList> &fields, const std::optional<qint64> &id, const std::optional<qint64> &limit)
Expand Down Expand Up @@ -106,18 +151,15 @@ QList<Record> QxSqlApi::listOneOrMoreRecords(const QString &table, const std::op
}

//================================================================
// QxSql
// QxSqlApiImpl
//================================================================
QxSql::QxSql(const QSqlDatabase &db)
: m_db(db)
{

}

QueryResult QxSql::query(const QString &query, const QVariantMap &params)
QueryResult QxSqlApiImpl::query(const QString &query, const QVariantMap &params)
{
// qDebug() << query << params;
QSqlQuery q(m_db);
q.prepare(query);
if (!q.prepare(query)) {
throw qf::core::Exception(q.lastError().text());
}
for (const auto &[key, val] : params.asKeyValueRange()) {
q.bindValue(QStringLiteral(":%1").arg(key), val);
}
Expand All @@ -126,7 +168,7 @@ QueryResult QxSql::query(const QString &query, const QVariantMap &params)
}
QueryResult result;
for (int i = 0; i < q.record().count(); ++i) {
result.columns.append(q.record().fieldName(i).toLower());
result.fields.append(DbField{.name = q.record().fieldName(i).toLower()});
}
while (q.next()) {
QList<QVariant> row;
Expand All @@ -138,55 +180,142 @@ QueryResult QxSql::query(const QString &query, const QVariantMap &params)
return result;
}

ExecResult QxSql::exec(const QString &query, const QVariantMap &params)
ExecResult QxSqlApiImpl::exec(const QString &query, const QVariantMap &params)
{
QSqlQuery q(m_db);
q.prepare(query);
if (!q.prepare(query)) {
throw qf::core::Exception(q.lastError().text());
}
for (const auto &[key, val] : params.asKeyValueRange()) {
q.bindValue(QStringLiteral(":%1").arg(key), val);
}
if (!q.exec()) {
throw qf::core::Exception(q.lastError().text());
}
ExecResult result;
result.rowsAffected = q.numRowsAffected();
result.numRowsAffected = q.numRowsAffected();
if (auto id = q.lastInsertId().toLongLong(); id > 0) {
result.lastInsertId = id;
}
return result;
}

qint64 QxSql::createRecord(const QString &table, const Record &record, const QString &issuer)
void QxSqlApiImpl::transaction(const QString &query, const QVariantList &params)
{
QxSqlApi::transaction(query, params, m_db);
}

//================================================================
// QxSql
//================================================================
QxSql::QxSql(const QString &issuer, const QSqlDatabase &db, QObject *parent)
: QObject(parent)
, m_issuer(issuer)
, m_sqlApi(db)
{
auto id = QxSqlApi::createRecord(table, record, issuer);
emit dbRecChng(qf::core::sql::QxRecChng{.table = table,
}

QueryResult QxSql::query(const QString &query, const QVariantMap &params)
{
return m_sqlApi.query(query, params);
}

ExecResult QxSql::exec(const QString &query, const QVariantMap &params)
{
return m_sqlApi.exec(query, params);
}

QList<Record> QxSql::listRecords(const QString &table, const std::optional<QStringList> &fields, const std::optional<qint64> &fromId, const std::optional<qint64> &limit)
{
return m_sqlApi.listRecords(table, fields, fromId, limit);
}

void QxSql::transaction(const QString &query, const QVariantList &params)
{
m_sqlApi.transaction(query, params);
}

qint64 QxSql::createRecord(const QString &table, const Record &record, QObject *source)
{
auto id = m_sqlApi.createRecord(table, record, m_issuer);
emit recChng(qf::core::sql::QxRecChng{.table = table,
.id = id,
.record = record,
.op = qf::core::sql::RecOp::Insert,
.issuer = issuer});
.issuer = m_issuer},
source);
return id;
}

bool QxSql::updateRecord(const QString &table, qint64 id, const Record &record, const QString &issuer)
std::optional<Record> QxSql::readRecord(const QString &table, qint64 id, const std::optional<QStringList> &fields)
{
auto ok = QxSqlApi::updateRecord(table, id, record, issuer);
return m_sqlApi.readRecord(table, id, fields);
}

bool QxSql::updateRecord(const QString &table, qint64 id, const Record &record, QObject *source)
{
auto ok = m_sqlApi.updateRecord(table, id, record, m_issuer);
if (ok) {
emit dbRecChng(qf::core::sql::QxRecChng{.table = table,
emit recChng(qf::core::sql::QxRecChng{.table = table,
.id = id,
.record = record,
.op = qf::core::sql::RecOp::Update,
.issuer = issuer});
.issuer = m_issuer},
source);
}
return ok;
}

bool QxSql::deleteRecord(const QString &table, qint64 id, const QString &issuer)
bool QxSql::deleteRecord(const QString &table, qint64 id, QObject *source)
{
auto ok = QxSqlApi::deleteRecord(table, id, issuer);
auto ok = m_sqlApi.deleteRecord(table, id, m_issuer);
if (ok) {
emit dbRecChng(qf::core::sql::QxRecChng{.table = table,
emit recChng(qf::core::sql::QxRecChng{.table = table,
.id = id,
.record = {},
.op = qf::core::sql::RecOp::Delete,
.issuer = issuer});
.issuer = m_issuer},
source);
}
return ok;
}

void QxSql::emitRecInserted(const QString &table, qint64 id, const QVariantMap &record, QObject *source)
{
emit recChng(qf::core::sql::QxRecChng{
.table = table,
.id = id,
.record = record,
.op = qf::core::sql::RecOp::Insert,
.issuer = m_issuer
}, source);
}

void QxSql::emitRecUpdated(const QString &table, qint64 id, const QVariantMap &record, QObject *source)
{
emit recChng(qf::core::sql::QxRecChng{
.table = table,
.id = id,
.record = record,
.op = qf::core::sql::RecOp::Update,
.issuer = m_issuer
}, source);
}

void QxSql::emitRecDeleted(const QString &table, qint64 id, QObject *source)
{
emit recChng(qf::core::sql::QxRecChng{
.table = table,
.id = id,
.record = {},
.op = qf::core::sql::RecOp::Delete,
.issuer = m_issuer
}, source);
}

void QxSql::emitRecChng(const core::sql::QxRecChng &recchng, QObject *source)
{
emit recChng(recchng, source);
}

}
Loading