From a9d93035b69c7baf9914f009cb2446034656eeef Mon Sep 17 00:00:00 2001 From: "pls.153" Date: Fri, 21 Mar 2025 12:15:16 +0100 Subject: [PATCH 1/3] correct wrong var name --- examples/meshtastic/cpp/qt.cpp | 10 +++++----- examples/meshtastic/lisp/db.lisp | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/meshtastic/cpp/qt.cpp b/examples/meshtastic/cpp/qt.cpp index c38903e..caf6077 100644 --- a/examples/meshtastic/cpp/qt.cpp +++ b/examples/meshtastic/cpp/qt.cpp @@ -171,7 +171,7 @@ QVariant QT::iniDb(const QVariant& vName) { return vName; } -QVariant QT::sqlQuery(const QVariant& vQuery, const QVariant& vValues, const QVariant& vRows) { +QVariant QT::sqlQuery(const QVariant& vQuery, const QVariant& vValues, const QVariant& vCols) { QVariantList results; QSqlQuery sqlQuery(db); if (db.open()) { @@ -182,11 +182,11 @@ QVariant QT::sqlQuery(const QVariant& vQuery, const QVariant& vValues, const QVa sqlQuery.addBindValue(value); } if (sqlQuery.exec()) { - auto rows = vRows.toInt(); + auto cols = vCols.toInt(); while (sqlQuery.next()) { - if (rows > 1) { + if (cols > 1) { QVariantList list; - for (auto r = 0; r < rows; r++) { + for (auto r = 0; r < cols; r++) { list << sqlQuery.value(r); } results << QVariant(list); @@ -194,7 +194,7 @@ QVariant QT::sqlQuery(const QVariant& vQuery, const QVariant& vValues, const QVa results << sqlQuery.value(0); } } - if (!rows && query.startsWith("insert", Qt::CaseInsensitive)) { + if (!cols && query.startsWith("insert", Qt::CaseInsensitive)) { results << sqlQuery.lastInsertId(); } db.close(); diff --git a/examples/meshtastic/lisp/db.lisp b/examples/meshtastic/lisp/db.lisp index bbf998a..ac93b49 100644 --- a/examples/meshtastic/lisp/db.lisp +++ b/examples/meshtastic/lisp/db.lisp @@ -3,9 +3,9 @@ (defvar *file* nil) (defun query (query &rest values) - (let ((rows (and (x:starts-with "select" query) + (let ((cols (and (x:starts-with "select" query) (1+ (count #\, (subseq query 0 (search "from" query))))))) - (qrun* (qt:sql-query qt:*cpp* query values rows)))) + (qrun* (qt:sql-query qt:*cpp* query values cols)))) (defun ini () (setf *file* (app:in-data-path "db")) From 121170fcbd73a94767174adf5bc317bbef7a523f Mon Sep 17 00:00:00 2001 From: "pls.153" Date: Fri, 21 Mar 2025 12:16:34 +0100 Subject: [PATCH 2/3] add new example 'qsqlite': use SQL from Lisp, load QML images from DB --- examples/qsqlite/app.asd | 9 ++ examples/qsqlite/app.pro | 127 +++++++++++++++++++++++++++++ examples/qsqlite/blob/logo-064.png | Bin 0 -> 4570 bytes examples/qsqlite/blob/logo-128.png | Bin 0 -> 11099 bytes examples/qsqlite/blob/logo-256.png | Bin 0 -> 37661 bytes examples/qsqlite/cpp/qt.cpp | 94 +++++++++++++++++++++ examples/qsqlite/cpp/qt.h | 36 ++++++++ examples/qsqlite/cpp/qt.pro | 27 ++++++ examples/qsqlite/lisp/db.lisp | 34 ++++++++ examples/qsqlite/lisp/main.lisp | 35 ++++++++ examples/qsqlite/lisp/package.lisp | 15 ++++ examples/qsqlite/lisp/qt.lisp | 19 +++++ examples/qsqlite/lisp/ui-vars.lisp | 9 ++ examples/qsqlite/make.lisp | 88 ++++++++++++++++++++ examples/qsqlite/qml/main.qml | 12 +++ examples/qsqlite/readme.md | 43 ++++++++++ examples/qsqlite/run.lisp | 29 +++++++ 17 files changed, 577 insertions(+) create mode 100644 examples/qsqlite/app.asd create mode 100644 examples/qsqlite/app.pro create mode 100755 examples/qsqlite/blob/logo-064.png create mode 100755 examples/qsqlite/blob/logo-128.png create mode 100755 examples/qsqlite/blob/logo-256.png create mode 100644 examples/qsqlite/cpp/qt.cpp create mode 100644 examples/qsqlite/cpp/qt.h create mode 100644 examples/qsqlite/cpp/qt.pro create mode 100644 examples/qsqlite/lisp/db.lisp create mode 100644 examples/qsqlite/lisp/main.lisp create mode 100644 examples/qsqlite/lisp/package.lisp create mode 100644 examples/qsqlite/lisp/qt.lisp create mode 100644 examples/qsqlite/lisp/ui-vars.lisp create mode 100644 examples/qsqlite/make.lisp create mode 100644 examples/qsqlite/qml/main.qml create mode 100644 examples/qsqlite/readme.md create mode 100644 examples/qsqlite/run.lisp diff --git a/examples/qsqlite/app.asd b/examples/qsqlite/app.asd new file mode 100644 index 0000000..880a383 --- /dev/null +++ b/examples/qsqlite/app.asd @@ -0,0 +1,9 @@ +(defsystem :app + :serial t + :depends-on () + :components ((:file "lisp/package") + (:file "lisp/qt") + (:file "lisp/ui-vars") + (:file "lisp/db") + (:file "lisp/main"))) + diff --git a/examples/qsqlite/app.pro b/examples/qsqlite/app.pro new file mode 100644 index 0000000..e6ef2eb --- /dev/null +++ b/examples/qsqlite/app.pro @@ -0,0 +1,127 @@ +LISP_FILES = $$files(lisp/*) app.asd make.lisp + +exists(/etc/sailfish-release) { + CONFIG += sfos +} + +android { + 32bit { + ECL = $$(ECL_ANDROID_32) + } else { + ECL = $$(ECL_ANDROID) + } + lisp.commands = $$ECL/../ecl-android-host/bin/ecl \ + -norc -shell $$PWD/make.lisp +} else:ios { + lisp.commands = $$(ECL_IOS)/../ecl-ios-host/bin/ecl \ + -norc -shell $$PWD/make.lisp +} else:unix { + lisp.commands = /usr/local/bin/ecl -shell $$PWD/make.lisp +} else:win32 { + lisp.commands = ecl.exe -shell $$PWD/make.lisp +} + +lisp.input = LISP_FILES + +win32: lisp.output = tmp/app.lib +!win32: lisp.output = tmp/libapp.a + +QMAKE_EXTRA_COMPILERS += lisp + +win32: PRE_TARGETDEPS = tmp/app.lib +!win32: PRE_TARGETDEPS = tmp/libapp.a + +QT += quick qml quickcontrols2 sql +TEMPLATE = app +CONFIG += c++17 no_keywords release +DEFINES = DESKTOP_APP INI_LISP QT_EXTENSION +INCLUDEPATH = /usr/local/include +LIBS = -L/usr/local/lib -lecl +DESTDIR = . +TARGET = app +OBJECTS_DIR = tmp +MOC_DIR = tmp + +linux: LIBS += -L../../../platforms/linux/lib +macx: LIBS += -L../../../platforms/macos/lib +win32: LIBS += -L../../../platforms/windows/lib + +win32 { + include(../../src/windows.pri) +} + +android { + DEFINES -= DESKTOP_APP + INCLUDEPATH = $$ECL/include + LIBS = -L$$ECL/lib -lecl + LIBS += -L../../../platforms/android/lib + + equals(QT_MAJOR_VERSION, 6) { + QT += core-private + } + lessThan(QT_MAJOR_VERSION, 6) { + QT += androidextras + } + + ANDROID_EXTRA_LIBS += $$ECL/lib/libecl.so + ANDROID_PACKAGE_SOURCE_DIR = ../platforms/android + + 32bit { + ANDROID_ABIS = "armeabi-v7a" + } else { + ANDROID_ABIS = "arm64-v8a" + } +} + +ios { + DEFINES -= DESKTOP_APP + INCLUDEPATH = $$(ECL_IOS)/include + LIBS = -L$$(ECL_IOS)/lib -lecl + LIBS += -leclatomic -leclffi -leclgc -leclgmp + LIBS += -L../../../platforms/ios/lib +} + +32bit { + android { + equals(QT_MAJOR_VERSION, 6) { + LIBS += -llqml32_armeabi-v7a + } + lessThan(QT_MAJOR_VERSION, 6) { + LIBS += -llqml32 + } + } + !android { + LIBS += -llqml32 + } + LIBS += -llisp32 +} else { + android { + equals(QT_MAJOR_VERSION, 6) { + LIBS += -llqml_arm64-v8a + } + lessThan(QT_MAJOR_VERSION, 6) { + LIBS += -llqml + } + } + !android { + LIBS += -llqml + } + LIBS += -llisp +} + +LIBS += -Ltmp -lapp + +HEADERS += \ + ../../src/cpp/main.h \ + cpp/qt.h + +SOURCES += \ + ../../src/cpp/main.cpp \ + cpp/qt.cpp + +RESOURCES += $$files(qml/*) +RESOURCES += $$files(i18n/*.qm) + +lupdate_only { + SOURCES += i18n/tr.h +} diff --git a/examples/qsqlite/blob/logo-064.png b/examples/qsqlite/blob/logo-064.png new file mode 100755 index 0000000000000000000000000000000000000000..03a9009ebb9b3e2a906755350d72e03140f0e105 GIT binary patch literal 4570 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 C%TUq) literal 0 HcmV?d00001 diff --git a/examples/qsqlite/cpp/qt.cpp b/examples/qsqlite/cpp/qt.cpp new file mode 100644 index 0000000..2bfdcfa --- /dev/null +++ b/examples/qsqlite/cpp/qt.cpp @@ -0,0 +1,94 @@ +#include "qt.h" +#include +#include +#include +#include + +QObject* ini() { + static QObject* qt = nullptr; + if (qt == nullptr) { + qt = new QT; + } + return qt; +} + +QT::QT() : QObject() { +} + +QVariant QT::dataPath(const QVariant& prefix) { + // for desktop + QString path = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); + path.truncate(path.lastIndexOf(QChar('/'))); + path.append(QStringLiteral("/lqml-qsqlite/") + prefix.toString()); + return path; +} + +// SQL + +QVariant QT::iniDb(const QVariant& vName, const QVariant& vQuickView) { + // add database image provider, in order to load images in QML directly from a SQL database + auto quickView = vQuickView.value(); + quickView->engine()->addImageProvider(QLatin1String("db"), new DatabaseImageProvider(this)); + // ini + db = QSqlDatabase::addDatabase("QSQLITE"); + db.setDatabaseName(vName.toString()); + return vName; +} + +QVariant QT::sqlQuery(const QVariant& vQuery, const QVariant& vValues, const QVariant& vCols) { + QVariantList results; + QSqlQuery sqlQuery(db); + if (db.open()) { + QString query = vQuery.toString(); + sqlQuery.prepare(vQuery.toString()); + const QVariantList values = vValues.value(); + for (auto value : values) { + sqlQuery.addBindValue(value); + } + if (sqlQuery.exec()) { + auto cols = vCols.toInt(); + while (sqlQuery.next()) { + if (cols > 1) { + QVariantList list; + for (auto r = 0; r < cols; r++) { + list << sqlQuery.value(r); + } + results << QVariant(list); + } else { + results << sqlQuery.value(0); + } + } + if (!cols && query.startsWith("insert", Qt::CaseInsensitive)) { + results << sqlQuery.lastInsertId(); + } + db.close(); + return results; + } + db.close(); + } + QString text; + if (sqlQuery.lastError().isValid()) { + text = sqlQuery.lastError().text(); + } else { + text = db.lastError().text(); + } + qDebug() << "SQL error:" << text; + return QVariant(); +} + +// database image provider + +QPixmap DatabaseImageProvider::requestPixmap(const QString& name, QSize* size, const QSize& requestedSize) { + auto result = qt->sqlQuery( + "select data from images where name = ?", + QVariantList() << name, + 1); // number of returned columns + QPixmap pixmap; + pixmap.loadFromData(result.value().first().toByteArray()); + *size = pixmap.size(); + if (requestedSize.isValid() && (pixmap.size() != requestedSize)) { + pixmap = pixmap.scaled(requestedSize); + } + return pixmap; +} + diff --git a/examples/qsqlite/cpp/qt.h b/examples/qsqlite/cpp/qt.h new file mode 100644 index 0000000..b96916f --- /dev/null +++ b/examples/qsqlite/cpp/qt.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +#ifdef Q_CC_MSVC +#define LIB_EXPORT __declspec(dllexport) +#else +#define LIB_EXPORT +#endif + +extern "C" { LIB_EXPORT QObject* ini(); } + +class QT : public QObject { + Q_OBJECT + +public: + Q_INVOKABLE QVariant dataPath(const QVariant&); + Q_INVOKABLE QVariant iniDb(const QVariant&, const QVariant&); + Q_INVOKABLE QVariant sqlQuery(const QVariant&, const QVariant&, const QVariant&); + + QT(); + + QSqlDatabase db; +}; + +class DatabaseImageProvider : public QQuickImageProvider { + +public: + DatabaseImageProvider(QT* _qt) : QQuickImageProvider(QQuickImageProvider::Pixmap), qt(_qt) {} + + QPixmap requestPixmap(const QString&, QSize*, const QSize&); + + QT* qt; +}; diff --git a/examples/qsqlite/cpp/qt.pro b/examples/qsqlite/cpp/qt.pro new file mode 100644 index 0000000..ea1d7c1 --- /dev/null +++ b/examples/qsqlite/cpp/qt.pro @@ -0,0 +1,27 @@ +QT += sql quick +TEMPLATE = lib +CONFIG += c++17 plugin release no_keywords +DEFINES += PLUGIN +INCLUDEPATH = /usr/local/include ../../../src/cpp +LIBS = -L/usr/local/lib -lecl +DESTDIR = ./ +TARGET = qt +OBJECTS_DIR = ./tmp/ +MOC_DIR = ./tmp/ + +HEADERS += qt.h +SOURCES += qt.cpp + +linux { + LIBS += -L../../../platforms/linux/lib +} + +macx { + LIBS += -L../../../platforms/macos/lib +} + +win32 { + include(../../../src/windows.pri) + + LIBS += -L../../../platforms/windows/lib +} diff --git a/examples/qsqlite/lisp/db.lisp b/examples/qsqlite/lisp/db.lisp new file mode 100644 index 0000000..e393afb --- /dev/null +++ b/examples/qsqlite/lisp/db.lisp @@ -0,0 +1,34 @@ +(in-package :db) + +(defvar *file* nil) + +(defun query (query &rest values) + (let ((rows (and (x:starts-with "select" query) + (1+ (count #\, (subseq query 0 (search "from" query))))))) + (qrun* (qt:sql-query qt:*cpp* query values rows)))) + +(defun ini () + (setf *file* (app:in-data-path "db")) + (ensure-directories-exist *file*) + (qt:ini-db qt:*cpp* (namestring *file*) *quick-view*) + (query "create table if not exists images (id integer primary key autoincrement, name text, data blob)")) + +(defun size () + (first (query "select count(*) from images"))) + +(defun save-image (name data) + "Inserts image NAME, DATA (vector of octets) and returns the new image ID." + (first (query "insert into images (name, data) values (?, ?)" + name data))) + +(defun load-images () + (query "select name, data from images order by id")) + +(defun delete-image (id) + (query "delete from images where id = ?" + id) + (values)) + +(defun delete-all-images () + (query "delete from images")) + diff --git a/examples/qsqlite/lisp/main.lisp b/examples/qsqlite/lisp/main.lisp new file mode 100644 index 0000000..75ce565 --- /dev/null +++ b/examples/qsqlite/lisp/main.lisp @@ -0,0 +1,35 @@ +(in-package :app) + +(defun ini () + (qt:ini) + (db:ini) + (populate-db) + ;; loads image directly from database, see 'cpp/qt.cpp::DatabaseImageProvider' + (q> |source| ui:*logo* "image://db/logo-128")) + +(defun in-data-path (&optional (file "") (prefix "data/")) + #+mobile + (merge-pathnames (x:cc prefix file)) + #-mobile + (x:cc (qrun* (qt:data-path qt:*cpp* prefix)) file)) + +(defun file-bytes (file) + (with-open-file (s file :element-type '(unsigned-byte 8)) + (let ((arr (make-array (file-length s) :element-type '(unsigned-byte 8)))) + (read-sequence arr s) + arr))) + +;; put images in database + +(defun populate-db () + (let ((files (sort (directory (merge-pathnames "blob/*.png")) + 'string< :key 'pathname-name))) + (when (/= (length files) (db:size)) + (if (probe-file (merge-pathnames "blob/")) + (progn + (db:delete-all-images) + (dolist (file files) + (db:save-image (pathname-name file) (file-bytes file))))) + (x:d "No 'blob/' directory found.")))) + +(qlater 'ini) diff --git a/examples/qsqlite/lisp/package.lisp b/examples/qsqlite/lisp/package.lisp new file mode 100644 index 0000000..a5f8a50 --- /dev/null +++ b/examples/qsqlite/lisp/package.lisp @@ -0,0 +1,15 @@ +(defpackage :app + (:use :cl :qml) + (:export + #:in-data-path)) + +(defpackage :db + (:use :cl :qml) + (:export + #:delete-image + #:delete-all-images + #:ini + #:load-images + #:save-image + #:size)) + diff --git a/examples/qsqlite/lisp/qt.lisp b/examples/qsqlite/lisp/qt.lisp new file mode 100644 index 0000000..2da0be3 --- /dev/null +++ b/examples/qsqlite/lisp/qt.lisp @@ -0,0 +1,19 @@ +(defpackage :qt + (:use :cl :qml) + (:export + #:*cpp* + #:data-path + #:ini + #:ini-db + #:sql-query)) + +(in-package :qt) + +(defvar *cpp* nil) + +(defun ini () + (setf *cpp* + #+qt-plugin (qload-c++ "cpp/qt") + #-qt-plugin (qfind-child nil "QT")) + (let ((*package* (find-package :qt))) + (define-qt-wrappers *cpp*))) diff --git a/examples/qsqlite/lisp/ui-vars.lisp b/examples/qsqlite/lisp/ui-vars.lisp new file mode 100644 index 0000000..5ec1f72 --- /dev/null +++ b/examples/qsqlite/lisp/ui-vars.lisp @@ -0,0 +1,9 @@ +(defpackage ui + (:use :cl :qml) + (:export + #:*logo*)) + +(in-package :ui) + +(defparameter *logo* "logo") + diff --git a/examples/qsqlite/make.lisp b/examples/qsqlite/make.lisp new file mode 100644 index 0000000..c6745ac --- /dev/null +++ b/examples/qsqlite/make.lisp @@ -0,0 +1,88 @@ +;; check target +(let ((arg (first (ext:command-args)))) + (mapc (lambda (name feature) + (when (search name arg) + (pushnew feature *features*))) + (list "/ecl-android" "/ecl-ios") + (list :android :ios))) + +#+(or android ios) +(pushnew :mobile *features*) + +(when (probe-file "/etc/sailfish-release") + (pushnew :sfos *features*)) + +(defun cc (&rest args) + (apply 'concatenate 'string args)) + +#+mobile +(defvar *assets* #+android "../platforms/android/assets/lib/" + #+ios "../platforms/ios/assets/Library/") + +#+mobile +(defun shell (command) + (ext:run-program "sh" (list "-c" command))) + +#+mobile +(let ((blob #+android *assets* + #+ios (cc *assets* "../Documents/"))) + (ensure-directories-exist blob) + (shell (cc "cp -r ../blob " blob))) + +(require :asdf) +(require :cmp) + +(push (merge-pathnames "../") + asdf:*central-registry*) + +(setf *default-pathname-defaults* + (truename (merge-pathnames "../../../"))) ; LQML root + +(defvar *current* + (let ((name (namestring *load-truename*))) + (subseq name + (length (namestring *default-pathname-defaults*)) + (1+ (position #\/ name :from-end t))))) + +(dolist (file (list "package" "x" "ecl-ext" "ini" "qml")) ; load LQML symbols + (load (merge-pathnames file "src/lisp/"))) + +(defun cc (&rest args) + (apply 'concatenate 'string args)) + +(progn + (defvar cl-user::*tr-path* (truename (cc *current* "i18n/"))) + (load "src/lisp/tr")) + +#-mobile +(asdf:make-build "app" + :monolithic t + :type :static-library + :move-here (cc *current* "build/tmp/") + :init-name "ini_app") + +#+mobile +(progn + (pushnew :interpreter *features*) + (defvar *asdf-system* "app") + (defvar *ql-libs* (cc *current* "ql-libs.lisp")) + (defvar *init-name* "ini_app") + (defvar *library-path* (format nil "~Abuild-~A/tmp/" + *current* + #+android "android" + #+ios "ios")) + (defvar *require* (list :ecl-curl)) + (load "platforms/shared/make")) + +;; rename lib +(let* ((from #-mobile (cc *current* (format nil "build/tmp/app--all-systems.~A" + #+msvc "lib" + #-msvc "a")) + #+mobile (cc *library-path* "app--all-systems.a")) + (to #-msvc "libapp.a" + #+msvc "app.lib") + (to* #-mobile (cc *current* "build/tmp/" to) + #+mobile (cc *library-path* to))) + (when (probe-file to*) + (delete-file to*)) + (rename-file from to)) diff --git a/examples/qsqlite/qml/main.qml b/examples/qsqlite/qml/main.qml new file mode 100644 index 0000000..3d11cb9 --- /dev/null +++ b/examples/qsqlite/qml/main.qml @@ -0,0 +1,12 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +Item { + width: 300 + height: 500 + + Image { + objectName: "logo" + anchors.centerIn: parent + } +} diff --git a/examples/qsqlite/readme.md b/examples/qsqlite/readme.md new file mode 100644 index 0000000..81dfa3e --- /dev/null +++ b/examples/qsqlite/readme.md @@ -0,0 +1,43 @@ + +Info +---- + +This is a simple example of: + +* using the **qsqlite** (Qt specific SQLite DB) library directly from Lisp +* providing a custom image provider, so we can directly load images in QML from + an SQL database + +The qsqlite library that comes with Qt has the advantage of being pulled in +automatically as a dependency, and behaving exactly the same, no matter what OS +is used. This is especially convenient on mobile. + +If you run the example with: +``` +$ lqml run.lisp -slime +``` +you can then try to change the image source from the REPL: +``` +(in-package :app) + +(q> |source| ui:*logo* "image://db/logo-256") +``` +This will load the image directly from the database. + + +Run +--- +``` +lqml run.lisp +``` +Optionally pass `-slime` to start a Swank server, and connect from Emacs with +`M-x slime-connect`. + +During development you can pass `-auto`, which will reload all QML files after +you made a change to any of them and saved it. For re-initialization after +reloading, file `lisp/qml-reload/on-reloaded` will be loaded. + +Closing the window quits the app. If you try to kill it with `ctrl-c`, you need +an additional `ctrl-d` to exit from ECL. To quit from Slime, do `(qq)` which is +short for `(qquit)`. + diff --git a/examples/qsqlite/run.lisp b/examples/qsqlite/run.lisp new file mode 100644 index 0000000..d0e113d --- /dev/null +++ b/examples/qsqlite/run.lisp @@ -0,0 +1,29 @@ +(in-package :qml-user) + +(pushnew :qt-plugin *features*) + +(require :asdf) + +(push (merge-pathnames "./") + asdf:*central-registry*) + +(asdf:operate 'asdf:load-source-op :app) + +(qset *quick-view* + |x| 75 + |y| 75) + +(defun option (name) + (find name (ext:command-args) :test 'search)) + +;;; trivial auto reload of all QML files after saving any change + +(when (option "-auto") + (load "lisp/qml-reload/auto-reload")) + +;;; for Slime after copying 'lqml-start-swank.lisp' from LQML sources +;;; to your Slime directory, which is assumed to be '~/slime/' + +(when (option "-slime") + (load "~/slime/lqml-start-swank")) ; for 'slime-connect' from Emacs + From 7a4918cc4b8b0735588c18b317db14e259a9afff Mon Sep 17 00:00:00 2001 From: "pls.153" Date: Fri, 21 Mar 2025 12:24:44 +0100 Subject: [PATCH 3/3] update readme --- examples/qsqlite/readme.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/examples/qsqlite/readme.md b/examples/qsqlite/readme.md index 81dfa3e..4ae0b4b 100644 --- a/examples/qsqlite/readme.md +++ b/examples/qsqlite/readme.md @@ -1,4 +1,3 @@ - Info ---- @@ -25,6 +24,21 @@ you can then try to change the image source from the REPL: This will load the image directly from the database. +Prepare +------- + +Please copy the app template files first: +``` +$ cd .. +$ ./copy.sh qsqlite +``` +For running on the desktop, you'll also need to compile the plugin: +``` +$ cd cpp +$ qmake; make +``` + + Run --- ```