add tutorial in 'Qt_EQL/' for accessing C++ apps from Lisp

This commit is contained in:
polos 2021-05-07 19:06:19 +02:00
parent 490c878dee
commit 28d5d133fb
6 changed files with 174 additions and 0 deletions

19
Qt_EQL/tutorial/README.md Normal file
View file

@ -0,0 +1,19 @@
## Info
This is a basic demo showing how to access a C++ app from Lisp, including the
creation of instances of a C++ class (the implementation of which is not really
elegant, but it works).
It also starts a simple REPL for playing around interactively.
## Build
qmake
make
## Run
./test

27
Qt_EQL/tutorial/main.cpp Normal file
View file

@ -0,0 +1,27 @@
#include "test.h"
#include <eql5/eql.h>
#include <QApplication>
#include <QLabel>
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
QLabel* main = new QLabel("<h2>Main Window</h2>");
main->setAlignment(Qt::AlignCenter);
main->resize(600, 400);
main->show();
// we need an instance for 'define-qt-wrappers' and 'new-instance' to work;
// we pass the main widget as parent and a unique 'objectName', so we can
// find it from Lisp
Test test(main, "test");
EQL eql;
EQL::eval("(in-package :eql-user)");
EQL::eval("(load \"test.lisp\")"); // will start a REPL
app.processEvents(); // needed for 'qlater' in 'test.lisp'
return 0; // no 'app.exec()' because of REPL
}

37
Qt_EQL/tutorial/test.cpp Normal file
View file

@ -0,0 +1,37 @@
#include "test.h"
#include <QtDebug>
#include <eql5/eql_fun.h>
Test::Test(QObject* parent, const QString& name) : QObject(parent) {
setObjectName(name);
}
QObject* Test::newInstance(QObject* parent, const QString& name) {
return new Test(parent, name);
}
QString Test::concat(const QStringList& list) {
return list.join(", ");
}
void Test::processData(cl_object data) {
// meant for passing complex Lisp data to be processed in C++
if(cl_listp(data) == ECL_T) {
for(cl_object l_dolist = data; cl_car(l_dolist) != ECL_NIL; l_dolist = cl_cdr(l_dolist)) {
cl_object l_el = cl_car(l_dolist);
cl_print(1, l_el);
}
cl_terpri(0);
}
}
void Test::printMe() {
// you may pass up to 10 arguments of any type found in
// '~/eql5/src/ecl_fun.cpp::toMetaArg()', wrapped in macro Q_ARG;
// C++ class instances are passed as pointers of a vanilla Qt class
// known to EQL5, here: 'QObject*'
eql_fun("eql-user:print-qt-object", Q_ARG(QObject*, this));
}

27
Qt_EQL/tutorial/test.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef APP_H
#define APP_H
#include <QObject>
#include <ecl/ecl.h>
class Test : public QObject {
Q_OBJECT
public:
Test(QObject*, const QString&);
// define function acting as constructor (callable from Lisp)
// N.B. return a vanilla Qt class (here: QObject*) known to EQL5
Q_INVOKABLE QObject* newInstance(QObject*, const QString&);
public Q_SLOTS:
// you may pass any type found in '~/eql5/src/ecl_fun.cpp::toMetaArg()'
QString concat(const QStringList&);
// pass Lisp data
void processData(cl_object);
// call back to Lisp
void printMe();
};
#endif

45
Qt_EQL/tutorial/test.lisp Normal file
View file

@ -0,0 +1,45 @@
(in-package :eql-user)
;; find main widget of app
(defvar *main-widget* (first (|topLevelWidgets.QApplication|)))
;; find 'Test' instance by its 'objectName'
(defvar *test* (qfind-child *main-widget* "test"))
;; the following defines generic functions for all Qt signals,
;; Qt slots and functions declared Q_INVOKABLE
(define-qt-wrappers *test*)
;; now we can call those functions
(defun test ()
;; make new instance
(format t "~%Creating instance of 'Test': ~A~%"
(new-instance *test* *main-widget* "test-1"))
;; call Qt slot
(format t "~%Calling 'Test::concat()': ~S~%"
(concat *test* (list "one" "two" "three")))
;; pass Lisp data
(format t "~%Processing complex Lisp data in C++:")
(process-data *test* (list 1 "virus" #\Esc 'the #(l a b)))
;; call C++ which will call back to Lisp
(print-me *test*)
(terpri))
(defun print-qt-object (object)
(format t "~%This is an instance of 'Test': ~S~%" object))
(defun repl ()
;; for playing around interactively (taken from '~/eql5/src/eql.cpp')
(setf *qtpl* t
*break-on-errors* t)
(when (directory (in-home "lib/ecl-readline.fas*"))
(load (x:check-recompile (in-home "lib/ecl-readline"))))
(qsingle-shot 500 'eql::start-read-thread)
(eql::exec-with-simple-restart)) ; start event loop
(progn
(test)
(qlater 'repl)) ; QLATER: don't block call from 'main.cpp'

19
Qt_EQL/tutorial/test.pro Normal file
View file

@ -0,0 +1,19 @@
QT += widgets printsupport uitools
TEMPLATE = app
CONFIG += no_keywords release
INCLUDEPATH += /usr/local/include
LIBS += -L/usr/local/lib -lecl -L$$[QT_INSTALL_LIBS] -leql5
TARGET = test
DESTDIR = ./
OBJECTS_DIR = ./tmp/
MOC_DIR = ./tmp/
win32 {
include(../../src/windows.pri)
}
HEADERS += test.h
SOURCES += test.cpp \
main.cpp