add help generated from doc-strings; some revisions

This commit is contained in:
pls.153 2022-01-22 15:07:55 +01:00
parent d7d4d980f4
commit 4625850752
12 changed files with 420 additions and 95 deletions

210
doc/help.htm Normal file
View file

@ -0,0 +1,210 @@
<!doctype html>
<html lang="en">
<head>
<title>Function List</title>
<meta charset="utf-8">
</head>
<body>
<pre>
<b>qload-c++ (library-name &optional unload)</b>
Loads a custom Qt/C++ plugin (see 'cpp-lib' in sources). The LIBRARY-NAME
has to be passed as path to the plugin, without file ending. This offers
a simple way to extend your application with your own Qt/C++ functions.
The plugin will be reloaded (if supported by the OS) every time you call
this function. If the UNLOAD argument is not NIL, the plugin will be
unloaded (if supported by the OS).
N.B: This works only for Qt6 functions with the following signature:
"QVariant foo(QVariant, ...)" ; max 10 QVariant arguments
Since a QVariant can also be of type QVariantList, this is a perfect fit
for (nested) Lisp lists.
(defparameter *c++* (qload-c++ "my-lib"))
(qapropos nil *c++*) ; documentation
(define-qt-wrappers *c++*) ; Lisp wrapper functions
<b>find-quick-item (object-name)</b>
Finds the first QQuickItem matching OBJECT-NAME. Locally set *ROOT-ITEM* if
you want to find items inside a specific item, like in a QML Repeater. See
also note in sources.
<b>pixel-ratio ()</b>
Returns the effective device pixel ratio.
<b>q! (method-name item/name &rest arguments)</b>
For calling methods of QML items.
(q! |requestPaint| *canvas*)
<b>q< (property-name item/name)</b>
Convenience macro for QML-GET. Use symbol instead of string name.
(q< |text| *label*)
(q< |font.pixelSize| *label*)
<b>q> (property-name item/name value)</b>
Convenience macro for QML-SET. Use symbol instead of string name.
(q> |text| *label* "greetings!")
<b>q>* (property-name item/name value)</b>
Convenience macro for QML-SET-ALL. Use symbol instead of string name. Sets
given property of all items sharing the same 'objectName'.
<b>qapropos (name &optional qobject/name)</b>
Searches properties, methods, signals, slots for NAME in QObject
(e.g. QQuickItem) passed as second argument. QQuickItems can also be passed
by their 'objectName'.
(qapropos nil *canvas*)
(qapropos "color")
<b>qapropos* (name &optional qobject/name)</b>
Similar to QAPROPOS, returning the results as nested list.
<b>qchildren (item/name)</b>
Like QML function children().
<b>qescape (string)</b>
Calls QString::toHtmlEscaped().
<b>qexec (&optional milliseconds)</b>
Calls QCoreApplication::exec(). Optionally pass the time in milliseconds
after which QEventLoop::exit() will be called. See also QSLEEP.
<b>qexit ()</b>
Calls QEventLoop::exit(), in order to exit event processing after a call
QEXEC with a timeout. Returns T if the event loop has effectively been
exited.
<b>qfind-child (qobject name)</b>
Calls QObject::findChild<QObject*>().
<b>qfrom-utf8 (byte-array)</b>
Returns the BYTE-ARRAY (vector of octets) converted using
QString::fromUtf8().
<b>qget (object name)</b>
Gets a Qt property. Enumerator values are returned as integer values.
Returns T as second return value for successful calls.
(qget *quick-view* |width|)
<b>qjs (method-name item/name &rest arguments</b>
Fast and convenient way to call JS functions defined in QML. You may pass
up to 10 arguments of the following types:
T, NIL, INTEGER, FLOAT, STRING, and (nested) lists of mentioned arguments.
N.B: Does not work with JS default arguments.
<b>qlater (function)</b>
Calls FUNCTION as soon as the Qt event loop is idle.
<b>qlog (arg1 &optional arg2 arg3...)</b>
For log messages on android.
(qlog 12)
(qlog "width" 10 "height" 20)
(qlog "x ~A y ~A" x y)
<b>qobject-name (qobject)</b>
Returns the QObject::objectName() of passed QOBJECT (FFI pointer).
<b>qobject-p (x)</b>
Tests if argument is of type QObject.
<b>qprocess-events ()</b>
Calls QCoreApplication::processEvents().
<b>qquit (&optional (exit-status 0) (kill-all-threads t))</b>
<b>qq</b>
Terminates LQML. Use this function instead of ECL (ext:quit) to quit
gracefully. Negative values for EXIT-STATUS will call C abort() instead of
normal program exit.
<b>qset (object name1 value1 &optional name2 value2...)</b>
Sets a Qt property. Enumerators have to be passed as integer values.
Returns T as second return value for successful calls.
(qset *quick-view* |x| 100 |y| 100)
<b>qsingle-shot (milliseconds function)</b>
A single shot timer similar to QTimer::singleShot().
(qsingle-shot 1000 'one-second-later)
<b>qsleep (seconds)</b>
Similar to SLEEP, but continuing to process Qt events.
<b>qversion ()</b>
Returns the LQML version number as 'year.month.counter'. The second
return value is the Qt version as returned by QLibraryInfo::version().
<b>root-item ()</b>
Returns the root item of the QQuickView.
<b>tr (source &optional context plural-number)</b>
Macro expanding to QTRANSLATE, which calls QCoreApplication::translate().
Both SOURCE and CONTEXT can be Lisp forms evaluating to constant strings
(at compile time). The CONTEXT argument defaults to the Lisp file name.
For the PLURAL-NUMBER, see Qt Assistant.
</pre>
</body>
</html>

View file

@ -4,6 +4,7 @@
#include "single_shot.h"
#include <QTimer>
#include <QLibrary>
#include <QLibraryInfo>
#include <QGuiApplication>
#include <QQuickItem>
#include <QQuickView>
@ -22,7 +23,7 @@ void iniCLFunctions() {
DEFUN ("%js", js2, 2)
DEFUN ("pixel-ratio", pixel_ratio, 0)
DEFUN ("%qapropos", qapropos2, 3)
DEFUN ("qchild-items", qchild_items, 1)
DEFUN ("qchildren", qchildren, 1)
DEFUN ("qescape", qescape, 1)
DEFUN ("%qexec", qexec2, 1)
DEFUN ("qexit", qexit, 0)
@ -31,7 +32,6 @@ void iniCLFunctions() {
DEFUN ("qfrom-utf8", qfrom_utf8, 1)
DEFUN ("%qinvoke-method", qinvoke_method2, 3)
DEFUN ("%qload-c++", qload_cpp, 2)
DEFUN ("qlocal8bit", qlocal8bit, 1)
DEFUN ("%qlog", qlog2, 1)
DEFUN ("%qml-get", qml_get2, 2)
DEFUN ("%qml-set", qml_set2, 3)
@ -43,7 +43,6 @@ void iniCLFunctions() {
DEFUN ("%qset", qset2, 2)
DEFUN ("%qsingle-shot", qsingle_shot2, 2)
DEFUN ("qtranslate", qtranslate, 3)
DEFUN ("qutf8", qutf8, 1)
DEFUN ("qversion", qversion, 0)
DEFUN ("%reload", reload2, 0)
DEFUN ("root-item", root_item, 0)
@ -55,6 +54,7 @@ void iniCLFunctions() {
// *** utils ***
void error_msg(const char* fun, cl_object l_args) {
// for error messages in ECL functions defined in C++
STATIC_SYMBOL_PKG (s_break_on_errors, "*BREAK-ON-ERRORS*", "QML")
if (cl_symbol_value(s_break_on_errors) != ECL_NIL) {
STATIC_SYMBOL_PKG (s_break, "%BREAK", "QML") // see "ini.lisp"
@ -79,11 +79,16 @@ void error_msg(const char* fun, cl_object l_args) {
// *** main functions ***
cl_object set_shutdown_p(cl_object l_obj) {
// for internal use
LQML::cl_shutdown_p = (l_obj != ECL_NIL);
ecl_return1(ecl_process_env(), l_obj);
}
cl_object qget2(cl_object l_obj, cl_object l_name) {
/// args: (object name)
/// Gets a Qt property. Enumerator values are returned as integer values.
/// Returns T as second return value for successful calls.
/// (qget *quick-view* |width|)
QObject* qobject = toQObjectPointer(l_obj);
if (ECL_STRINGP(l_name) && (qobject != nullptr)) {
const QMetaObject* mo = qobject->metaObject();
@ -101,6 +106,10 @@ cl_object qget2(cl_object l_obj, cl_object l_name) {
}
cl_object qset2(cl_object l_obj, cl_object l_args) {
/// args: (object name1 value1 &optional name2 value2...)
/// Sets a Qt property. Enumerators have to be passed as integer values.
/// Returns T as second return value for successful calls.
/// (qset *quick-view* |x| 100 |y| 100)
QObject* qobject = toQObjectPointer(l_obj);
if (qobject != nullptr) {
const QMetaObject* mo = qobject->metaObject();
@ -131,6 +140,8 @@ fail:
}
cl_object qfind_child(cl_object l_obj, cl_object l_name) {
/// args: (qobject name)
/// Calls QObject::findChild<QObject*>().
ecl_process_env()->nvalues = 1;
QString name(toQString(l_name));
if (!name.isEmpty()) {
@ -148,6 +159,7 @@ cl_object qfind_child(cl_object l_obj, cl_object l_name) {
}
cl_object qfind_children2(cl_object l_obj, cl_object l_name, cl_object l_class) {
// for internal use
ecl_process_env()->nvalues = 1;
QString objectName(toQString(l_name));
QByteArray className(toCString(l_class));
@ -169,7 +181,9 @@ cl_object qfind_children2(cl_object l_obj, cl_object l_name, cl_object l_class)
return ECL_NIL;
}
cl_object qchild_items(cl_object l_item) {
cl_object qchildren(cl_object l_item) {
/// args: (item/name)
/// Like QML function children().
ecl_process_env()->nvalues = 1;
QObject* qobject = toQObjectPointer(l_item);
QQuickItem* item = qobject_cast<QQuickItem*>(qobject); // type check
@ -183,11 +197,25 @@ cl_object qchild_items(cl_object l_item) {
l_children = cl_nreverse(l_children);
return l_children;
}
error_msg("QCHILD-ITEMS", LIST1(l_item));
error_msg("QCHILDREN", LIST1(l_item));
return ECL_NIL;
}
cl_object qload_cpp(cl_object l_lib_name, cl_object l_unload) { /// qload-c++
/// args: (library-name &optional unload)
/// Loads a custom Qt/C++ plugin (see 'cpp-lib' in sources). The LIBRARY-NAME
/// has to be passed as path to the plugin, without file ending. This offers
/// a simple way to extend your application with your own Qt/C++ functions.
/// The plugin will be reloaded (if supported by the OS) every time you call
/// this function. If the UNLOAD argument is not NIL, the plugin will be
/// unloaded (if supported by the OS).
/// N.B: This works only for Qt6 functions with the following signature:
/// "QVariant foo(QVariant, ...)" ; max 10 QVariant arguments
/// Since a QVariant can also be of type QVariantList, this is a perfect fit
/// for (nested) Lisp lists.
/// (defparameter *c++* (qload-c++ "my-lib"))
/// (qapropos nil *c++*) ; documentation
/// (define-qt-wrappers *c++*) ; Lisp wrapper functions
static QHash<QString, QLibrary*> libraries;
QString libName = toQString(l_lib_name);
bool unload = (l_unload != ECL_NIL);
@ -235,6 +263,7 @@ cl_object qload_cpp(cl_object l_lib_name, cl_object l_unload) { /// qload-c++
// *** convenience functions ***
cl_object qtranslate(cl_object l_con, cl_object l_src, cl_object l_n) {
// called by QML:TR
QByteArray context(toQString(l_con).toUtf8());
QByteArray source(toQString(l_src).toUtf8());
int n = toInt(l_n);
@ -248,34 +277,32 @@ cl_object qtranslate(cl_object l_con, cl_object l_src, cl_object l_n) {
ecl_return1(ecl_process_env(), l_ret);
}
cl_object qlocal8bit(cl_object l_str) {
// returns 'ecl_simple_base_string', not Unicode
cl_object l_ret = from_cstring(toQString(l_str).toLocal8Bit());
ecl_return1(ecl_process_env(), l_ret);
}
cl_object qutf8(cl_object l_str) {
// returns 'ecl_simple_base_string', not Unicode
cl_object l_ret = from_cstring(toQString(l_str).toUtf8());
ecl_return1(ecl_process_env(), l_ret);
}
cl_object qfrom_utf8(cl_object l_ba) {
/// args: (byte-array)
/// Returns the BYTE-ARRAY (vector of octets) converted using
/// QString::fromUtf8().
cl_object l_ret = from_qstring(QString::fromUtf8(toQByteArray(l_ba)));
ecl_return1(ecl_process_env(), l_ret);
}
cl_object qescape(cl_object l_str) {
/// args: (string)
/// Calls QString::toHtmlEscaped().
cl_object l_ret = from_qstring(toQString(l_str).toHtmlEscaped());
ecl_return1(ecl_process_env(), l_ret);
}
cl_object qprocess_events() {
QGuiApplication::processEvents();
/// args: ()
/// Calls QCoreApplication::processEvents().
QCoreApplication::processEvents();
ecl_return1(ecl_process_env(), ECL_T);
}
cl_object qexec2(cl_object l_milliseconds) {
/// args: (&optional milliseconds)
/// Calls QCoreApplication::exec(). Optionally pass the time in milliseconds
/// after which QEventLoop::exit() will be called. See also QSLEEP.
ecl_process_env()->nvalues = 1;
if (l_milliseconds != ECL_NIL) {
static QTimer* timer = 0;
@ -290,11 +317,15 @@ cl_object qexec2(cl_object l_milliseconds) {
return l_milliseconds;
}
QCoreApplication::exit(); // prevent "the event loop is already running"
QGuiApplication::exec();
QCoreApplication::exec();
return ECL_T;
}
cl_object qexit() {
/// args: ()
/// Calls QEventLoop::exit(), in order to exit event processing after a call
/// QEXEC with a timeout. Returns T if the event loop has effectively been
/// exited.
ecl_process_env()->nvalues = 1;
if (LQML::eventLoop) {
if (LQML::eventLoop->isRunning()) {
@ -306,6 +337,9 @@ cl_object qexit() {
}
cl_object qsingle_shot2(cl_object l_msec, cl_object l_fun) {
/// args: (milliseconds function)
/// A single shot timer similar to QTimer::singleShot().
/// (qsingle-shot 1000 'one-second-later)
ecl_process_env()->nvalues = 1;
if (l_fun != ECL_NIL) {
new SingleShot(toInt(l_msec), l_fun); // see "delete this;" in "single_shot.h"
@ -316,12 +350,16 @@ cl_object qsingle_shot2(cl_object l_msec, cl_object l_fun) {
}
cl_object qversion() {
/// args: ()
/// Returns the LQML version number as 'year.month.counter'. The second
/// return value is the Qt version as returned by QLibraryInfo::version().
cl_object l_ret1 = from_cstring(LQML::version);
cl_object l_ret2 = from_cstring(qVersion());
cl_object l_ret2 = from_qstring(QLibraryInfo::version().toString());
ecl_return2(ecl_process_env(), l_ret1, l_ret2);
}
cl_object qrun_on_ui_thread2(cl_object l_function_or_closure, cl_object l_blocking) {
// for internal use, you should never need to call it explicitely
ecl_process_env()->nvalues = 1;
if (l_function_or_closure != ECL_NIL) {
QObject o;
@ -344,17 +382,18 @@ cl_object qrun_on_ui_thread2(cl_object l_function_or_closure, cl_object l_blocki
}
cl_object qlog2(cl_object l_msg) {
// for android logging only; see 'ini.lisp::qlog' and 'lqml.cpp::logMessageHandler'
// called by QML:QLOG
// for android logging only; see also 'lqml.cpp::logMessageHandler'
qDebug() << toQString(l_msg);
ecl_return1(ecl_process_env(), ECL_NIL);
}
cl_object qinvoke_method2(cl_object l_obj, cl_object l_name, cl_object l_args) {
// max. 10 arguments
// supported argument types: T, NIL, INTEGER, FLOAT, STRING,
// (nested) LIST of mentioned arguments
//
// N.B. does not support default arguments if used to call JS functions
// for internal use: this is used to call user defined JS functions, and to
// call user defined Qt/C++ plugin functions.
// Max. 10 arguments of type T, NIL, INTEGER, FLOAT, STRING, (nested) LIST of
// mentioned arguments. On Qt side, only QVariant arguments are allowed.
// N.B. does not support default arguments, if used to call JS functions
ecl_process_env()->nvalues = 1;
const int MAX = 10;
QVariant arg[MAX];
@ -385,6 +424,7 @@ cl_object qinvoke_method2(cl_object l_obj, cl_object l_name, cl_object l_args) {
}
cl_object js2(cl_object l_item, cl_object l_str) {
// called by function QML:JS
ecl_process_env()->nvalues = 1;
QObject* qobject = toQObjectPointer(l_item);
if (qobject != nullptr) {
@ -397,6 +437,7 @@ cl_object js2(cl_object l_item, cl_object l_str) {
}
cl_object qml_get2(cl_object l_item, cl_object l_name) {
// called by QML:QML-GET
QObject* qobject = toQObjectPointer(l_item);
QByteArray name = toCString(l_name);
if ((qobject != nullptr) && !name.isEmpty()) {
@ -411,6 +452,7 @@ cl_object qml_get2(cl_object l_item, cl_object l_name) {
}
cl_object qml_set2(cl_object l_item, cl_object l_name, cl_object l_value) {
// called by QML:QML-SET
ecl_process_env()->nvalues = 1;
QObject* qobject = toQObjectPointer(l_item);
QByteArray name = toCString(l_name);
@ -427,6 +469,8 @@ cl_object qml_set2(cl_object l_item, cl_object l_name, cl_object l_value) {
}
cl_object qobject_name(cl_object l_obj) {
/// args: (qobject)
/// Returns the QObject::objectName() of passed QOBJECT (FFI pointer).
ecl_process_env()->nvalues = 1;
QObject* qobject = toQObjectPointer(l_obj);
if (qobject != nullptr) {
@ -438,16 +482,19 @@ cl_object qobject_name(cl_object l_obj) {
}
cl_object root_item() {
/// args: ()
/// Returns the root item of the QQuickView.
ecl_process_env()->nvalues = 1;
cl_object l_ret = from_qobject_pointer(LQML::quickView->rootObject());
ecl_return1(ecl_process_env(), l_ret);
}
cl_object qquit2(cl_object l_status) {
// called by QML:QQUIT
int s = toInt(l_status);
qGuiApp->quit();
cl_shutdown();
LQML::cl_shutdown_p = true;
int s = toInt(l_status);
if (s < 0) {
abort();
} else {
@ -457,11 +504,14 @@ cl_object qquit2(cl_object l_status) {
}
cl_object pixel_ratio() {
/// args: ()
/// Returns the effective device pixel ratio.
cl_object l_ret = ecl_make_doublefloat(LQML::quickView->effectiveDevicePixelRatio());
ecl_return1(ecl_process_env(), l_ret);
}
cl_object reload2() {
// called by QML:RELOAD
LQML::quickView->engine()->clearComponentCache();
QUrl source(LQML::quickView->source());
LQML::quickView->setSource(source);
@ -564,6 +614,7 @@ static cl_object collectInfo(const QByteArray& type,
}
cl_object qapropos2(cl_object l_search, cl_object l_obj, cl_object l_no_offset) {
// called by QML:QAPROPOS
ecl_process_env()->nvalues = 1;
QByteArray search;
if (ECL_STRINGP(l_search)) {

View file

@ -52,7 +52,7 @@ QT_BEGIN_NAMESPACE
cl_object js2 (cl_object, cl_object);
cl_object pixel_ratio ();
cl_object qapropos2 (cl_object, cl_object, cl_object);
cl_object qchild_items (cl_object);
cl_object qchildren (cl_object);
cl_object qescape (cl_object);
cl_object qexec2 (cl_object);
cl_object qexit ();
@ -61,7 +61,6 @@ cl_object qfind_children2 (cl_object, cl_object, cl_object);
cl_object qfrom_utf8 (cl_object);
cl_object qinvoke_method2 (cl_object, cl_object, cl_object);
cl_object qload_cpp (cl_object, cl_object);
cl_object qlocal8bit (cl_object);
cl_object qlog2 (cl_object);
cl_object qml_get2 (cl_object, cl_object);
cl_object qml_set2 (cl_object, cl_object, cl_object);
@ -73,7 +72,6 @@ cl_object qget2 (cl_object, cl_object);
cl_object qset2 (cl_object, cl_object);
cl_object qsingle_shot2 (cl_object, cl_object);
cl_object qtranslate (cl_object, cl_object, cl_object);
cl_object qutf8 (cl_object);
cl_object qversion ();
cl_object reload2 ();
cl_object root_item ();

View file

@ -48,7 +48,8 @@ LQML::LQML(int argc, char* argv[], QQuickView* view) : QObject() {
iniCLFunctions();
ecl_init_module(NULL, ini_LQML);
eval("(in-package :qml-user)");
eval(QString("(setf *quick-view* (make-qobject %1))").arg((quintptr)view));
eval(QString("(setf *quick-view* (make-qobject %1))")
.arg(reinterpret_cast<quintptr>(view)));
}
LQML::~LQML() {

View file

@ -28,6 +28,7 @@ int main(int argc, char* argv[]) {
//app.setOrganizationName("MyProject");
//app.setOrganizationDomain("my.org");
app.setApplicationName(QFileInfo(app.applicationFilePath()).baseName());
QStringList arguments(QCoreApplication::arguments());
QQuickView view;
ADD_MACOS_BUNDLE_IMPORT_PATH
@ -41,7 +42,6 @@ int main(int argc, char* argv[]) {
view.connect(view.engine(), &QQmlEngine::quit, &app, &QCoreApplication::quit);
LQML lqml(argc, argv, &view);
QStringList arguments(QCoreApplication::arguments());
if (arguments.contains("-v") || arguments.contains("--version")) {
lqml.printVersion();
std::cout << std::endl;
@ -49,16 +49,25 @@ int main(int argc, char* argv[]) {
}
new QQmlFileSelector(view.engine(), &view);
QUrl url("qrc:///qml/main.qml");
if (!QFile::exists(url.fileName())) {
url = "qml/main.qml";
QString qml("qml/main.qml");
QUrl url("qrc:///" + qml);
bool set = false;
if (QFile::exists(url.fileName())) {
set = true;
} else {
url = QUrl::fromLocalFile(qml);
if (QFile::exists(QDir::currentPath() + "/" + qml)) {
set = true;
}
}
view.setSource(url);
if (view.status() == QQuickView::Error) {
return -1;
if (set) {
view.setSource(url);
if (view.status() == QQuickView::Error) {
return -1;
}
view.setResizeMode(QQuickView::SizeRootObjectToView);
QTimer::singleShot(0, &view, &QQuickView::show);
}
view.setResizeMode(QQuickView::SizeRootObjectToView);
QTimer::singleShot(0, &view, &QQuickView::show);
// load .eclrc
if (arguments.contains("-norc")) {

View file

@ -246,7 +246,9 @@ cl_object from_qvariant(const QVariant& var) {
cl_object from_qobject_pointer(QObject* qobject) {
STATIC_SYMBOL_PKG (s_make_qobject, "MAKE-QOBJECT", "QML") // see 'ini.lisp'
return cl_funcall(2, s_make_qobject, ecl_make_unsigned_integer((quintptr)qobject));
return cl_funcall(2,
s_make_qobject,
ecl_make_unsigned_integer(reinterpret_cast<quintptr>(qobject)));
}
QT_END_NAMESPACE

View file

@ -20,7 +20,7 @@ QObject* iniQml() {
static QVariant qmlApply(QObject* caller, const QString& function, const QVariantList& arguments) {
QVariant var =
ecl_fun("qml:qml-apply",
QVariant((quintptr)caller),
QVariant(reinterpret_cast<quintptr>(caller)),
QVariant(function),
QVariant(arguments));
QString str(var.toString());

View file

@ -1,5 +1,3 @@
#undef SLOT
#include "qt_ecl.h"
#include "marshal.h"
#include "ecl_ext.h"

View file

@ -1,8 +1,6 @@
#ifndef SINGLE_SHOT_H
#define SINGLE_SHOT_H
#undef SLOT
#include <ecl/ecl.h>
#include <QObject>

View file

@ -1,21 +1,31 @@
#+ecl
(si::trap-fpe t nil) ; ignore floating point exceptions (they happen on Qt side)
;;; doc-string note: documentation is added where a function is defined;
;;; sometimes this is in file 'ecl_ext.cpp'
(si::trap-fpe t nil) ; ignore floating point exceptions (caused on Qt side)
(in-package :qml)
(defvar *break-on-errors* t)
(defvar *break-on-errors* t
"If T, call (BREAK) on errors inside of LQML functions defined in C++.")
(defun make-qobject (address)
;; for internal use
(ffi:make-pointer address :pointer-void))
(defun qobject-p (x)
"args: (x)
Tests if argument is of type QObject."
(eql 'si:foreign-data (type-of x)))
(defmacro alias (s1 s2)
`(setf (symbol-function ',s1) (function ,s2)))
(defmacro ! (fun &rest args)
`(qfun ,(cadar args) ,fun ,@(rest args)))
(defmacro ! (fun qobject &rest args)
;; legacy, should not be needed, use DEFINE-QT-WRAPPERS instead
;; usage:
;; (! "myFunction" *cpp* 1 2 3)
;; (! |myFunction| *cpp* 1 2 3)
`(qfun ,qobject ,(if (stringp fun) fun (symbol-name fun)) ,@(rest args)))
(defun %reference-name ()
(format nil "%~A%" (gensym)))
@ -24,6 +34,8 @@
(%qexec ms))
(defun qsleep (seconds)
"args: (seconds)
Similar to SLEEP, but continuing to process Qt events."
(%qexec (floor (* 1000 seconds)))
nil)
@ -35,9 +47,11 @@
(%qsingle-shot ,milliseconds (setf (symbol-function (intern ,(%reference-name))) ; lambda
,function))))
`(qrun (lambda ()
(%qsingle-shot ,milliseconds ,function))))) ; 'foo
(%qsingle-shot ,milliseconds ,function))))) ; 'foo
(defmacro qlater (function)
"args: (function)
Calls FUNCTION as soon as the Qt event loop is idle."
`(qsingle-shot 0 ,function))
(defun %ensure-persistent-function (fun)
@ -50,12 +64,16 @@
fun))))
(defun %make-vector ()
;; for internal use (called from 'ecl_ext.cpp')
(make-array 0 :adjustable t :fill-pointer t))
(defun %break (&rest args)
;; for internal use (called from 'ecl_ext.cpp')
(apply 'break args))
(defun ignore-io-streams ()
"Needed on Windows to prevent crash on print output (for apps without
a console window)."
(setf *standard-output* (make-broadcast-stream)
*trace-output* *standard-output*
*error-output* *standard-output*
@ -63,6 +81,11 @@
*standard-output*)))
(defmacro tr (source &optional context (plural-number -1))
"args: (source &optional context plural-number)
Macro expanding to QTRANSLATE, which calls QCoreApplication::translate().
Both SOURCE and CONTEXT can be Lisp forms evaluating to constant strings
(at compile time). The CONTEXT argument defaults to the Lisp file name.
For the PLURAL-NUMBER, see Qt Assistant."
;; see compiler-macro in "tr.lisp"
(let ((source* (ignore-errors (eval source)))
(context* (ignore-errors (eval context))))
@ -87,8 +110,12 @@
(%qload-c++ library-name unload))
(defun define-qt-wrappers (qt-library &rest what)
;; N.B. This works only for Qt6 functions with the following signature:
;; "QVariant foo(QVariant, ...)" ; max 10 QVariant arguments
"args: (qt-library &rest what)
Defines Lisp methods for all Qt methods/signals/slots of given library,
previously loaded with QLOAD-C++.
(define-qt-wrappers *c++*) ; generate wrappers
(define-qt-wrappers *c++* :methods) ; Qt methods only (no slots/signals)
(my-qt-function *c++* x y) ; call from Lisp"
(let ((all-functions (qapropos* nil (ensure-qt-object qt-library)))
(lispify (not (find :do-not-lispify what))))
(setf what (remove-if (lambda (x) (find x '(:do-not-lispify t)))
@ -119,6 +146,7 @@
(%qinvoke-method object ,qt-name arguments)))))))))
(defun qinvoke-method (object function-name &rest arguments)
;; for internal use
(%qinvoke-method object function-name arguments))
(defmacro qget (object name)
@ -137,11 +165,13 @@
arguments))))
(defun qrun-on-ui-thread (function &optional (blocking t))
;; for internal use
(%qrun-on-ui-thread function blocking))
(defvar *gui-thread* mp:*current-process*)
(defmacro qrun-on-ui-thread* (&body body)
;; for internal use
(let ((values (gensym)))
`(if (eql *gui-thread* mp:*current-process*)
,(if (second body)
@ -158,6 +188,11 @@
`(qrun-on-ui-thread* ,@body))
(defun qquit (&optional (exit-status 0) (kill-all-threads t))
"args: (&optional (exit-status 0) (kill-all-threads t))
alias: qq
Terminates LQML. Use this function instead of ECL (ext:quit) to quit
gracefully. Negative values for EXIT-STATUS will call C abort() instead of
normal program exit."
(declare (ignore kill-all-threads)) ; only here to be equivalent to EXT:QUIT
(assert (typep exit-status 'fixnum))
(%qquit exit-status))
@ -169,9 +204,11 @@
;;; for android logging
(defun qlog (arg1 &rest args)
;; (qlog 12)
;; (qlog 1 "plus" 2 "gives" 3)
;; (qlog "x ~A y ~A" x y)
"args: (arg1 &optional arg2 arg3...)
For log messages on android.
(qlog 12)
(qlog \"width\" 10 \"height\" 20)
(qlog \"x ~A y ~A\" x y)"
(%qlog (if (and (stringp arg1)
(find #\~ arg1))
(apply 'format nil arg1 args)

View file

@ -13,6 +13,7 @@
#:pixel-ratio
#:qapropos
#:qapropos*
#:qfind-child
#:qml-call
#:qml-get
#:qml-set
@ -22,20 +23,16 @@
#:q>
#:q>*
#:qjs
#+linux
#:qchild-items
#:qchildren
#:qescape
#:qexec
#:qexit
#:qfind-child
#:qfind-children
#:qfrom-utf8
#:qfun
#:qget
#:qset
#:qlater
#:qload-c++
#:qlocal8bit
#:qlog
#:qobject-p
#:qprocess-events
@ -48,13 +45,11 @@
#:qsingle-shot
#:qsleep
#:qtranslate
#:qutf8
#:qversion
#:qvariant-from-value
#:qvariant-value
#:root-item
#:reload
#:tr))
#:tr
#:!))
(defpackage :qml-user
(:use :common-lisp :qml))

View file

@ -49,9 +49,9 @@
(print-js-readably object)))
(defun qml-apply (caller function arguments)
"Every 'Lisp.call()' or 'Lisp.apply()' function call in QML will call this
function. The variable *CALLER* will be bound to the calling QQuickItem, if
passed with 'this' as first argument to 'Lisp.call()' / 'Lisp.apply()'."
;; Every 'Lisp.call()' or 'Lisp.apply()' function call in QML will call this
;: function. The variable *CALLER* will be bound to the calling QQuickItem,
;; if passed with 'this' as first argument to 'Lisp.call()' / 'Lisp.apply()'.
(let* ((*caller* (if (zerop caller) ; don't change LET*
*caller*
(make-qobject caller)))
@ -64,7 +64,8 @@
;;; utils
(defun find-quick-item (object-name)
"Finds the first QQuickItem matching OBJECT-NAME. Locally set *ROOT-ITEM* if
"args: (object-name)
Finds the first QQuickItem matching OBJECT-NAME. Locally set *ROOT-ITEM* if
you want to find items inside a specific item, like in a QML Repeater. See
also note in sources."
;;
@ -94,6 +95,7 @@
(qfind-child parent object-name)))))
(defun quick-item (item/name)
;; for internal use
(cond ((stringp item/name)
(find-quick-item item/name))
((qobject-p item/name)
@ -102,52 +104,55 @@
(root-item))))
(defun children (item/name)
"Like QML function 'children'."
"args: (item/name)
Like QML function 'children'."
(qrun* (qchild-items (quick-item item/name))))
(defun reload ()
"Reloads all QML files, clearing the cache."
"args: ()
Reloads all QML files, clearing the cache."
(qrun* (%reload)))
;;; get/set QML properties, call QML methods (through JS)
(defun qml-get (item/name property-name)
"Gets QQmlProperty of either ITEM or first object matching NAME."
;; see Q<
(qrun* (%qml-get (quick-item item/name) property-name)))
(defun qml-set (item/name property-name value)
"Sets QQmlProperty of either ITEM, or first object matching NAME.
Returns T on success."
;; see Q>
(qrun* (%qml-set (quick-item item/name) property-name value)))
(defun qml-set-all (name property-name value)
"Sets QQmlProperty of all objects matching NAME."
;; see Q>*
(assert (stringp name))
(qrun* (dolist (item (qfind-children (root-item) name))
(qml-set item property-name value))))
(defmacro q! (method-name item/name &rest arguments)
"Convenience macro for QML-CALL. Use symbol instead of string name."
`(js ,item/name ,(symbol-name method-name) ,@arguments))
(defmacro q> (property-name item/name value)
"Convenience macro for QML-SET. Use symbol instead of string name."
`(qml-set ,item/name ,(symbol-name property-name) ,value))
;;; convenience macros: re-arrange arguments to put the name first, and use
;;; a symbol instead of the string name, so we can use auto completion
(defmacro q< (property-name item/name)
"Convenience macro for QML-GET. Use symbol instead of string name."
"args: (property-name item/name)
Convenience macro for QML-GET. Use symbol instead of string name.
(q< |text| *label*)
(q< |font.pixelSize| *label*)"
`(qml-get ,item/name ,(symbol-name property-name)))
(defmacro q> (property-name item/name value)
"args: (property-name item/name value)
Convenience macro for QML-SET. Use symbol instead of string name.
(q> |text| *label* \"greetings!\")"
`(qml-set ,item/name ,(symbol-name property-name) ,value))
(defmacro q>* (property-name item/name value)
"Convenience macro for QML-SET-ALL. Use symbol instead of string name."
"args: (property-name item/name value)
Convenience macro for QML-SET-ALL. Use symbol instead of string name. Sets
given property of all items sharing the same 'objectName'."
`(qml-set-all ,item/name ,(symbol-name property-name) ,value))
;;; JS
(defun js (item/name fun &rest arguments)
"Evaluates a JS string, with 'this' bound to either ITEM, or first object
matching NAME. Use this function instead of the (faster) QJS if you need to
evaluate generic JS code, or for JS functions with default arguments."
;; for internal use, see macro Q!
(qrun* (%js (quick-item item/name)
(apply 'format nil
(format nil "~A(~A)"
@ -156,13 +161,26 @@
(mapcar 'js-arg arguments)))))
(defun js-arg (object)
"Used for arguments in function JS."
;; for arguments in function JS
(if (stringp object)
object
(with-output-to-string (*standard-output*)
(print-js-readably object))))
(defmacro q! (method-name item/name &rest arguments)
"args: (method-name item/name &rest arguments)
For calling methods of QML items.
(q! |requestPaint| *canvas*)"
`(js ,item/name ,(symbol-name method-name) ,@arguments))
;;; JS calls
(defmacro qjs (method-name item/name &rest arguments)
"args: (method-name item/name &rest arguments
Fast and convenient way to call JS functions defined in QML. You may pass
up to 10 arguments of the following types:
T, NIL, INTEGER, FLOAT, STRING, and (nested) lists of mentioned arguments.
N.B: Does not work with JS default arguments."
`(qrun* (qfun (quick-item ,item/name)
,(if (symbolp method-name)
(symbol-name method-name)
@ -176,8 +194,14 @@
x
(quick-item x)))
(defun qapropos (name &optional class offset)
(dolist (sub1 (%qapropos (%string-or-nil name) (%to-qobject class) offset))
(defun qapropos (name &optional qobject/name offset)
"args: (name &optional qobject/name)
Searches properties, methods, signals, slots for NAME in QObject
(e.g. QQuickItem) passed as second argument. QQuickItems can also be passed
by their 'objectName'.
(qapropos nil *canvas*)
(qapropos \"color\")"
(dolist (sub1 (%qapropos (%string-or-nil name) (%to-qobject qobject/name) offset))
(format t "~%~%~A~%" (first sub1))
(dolist (sub2 (rest sub1))
(format t "~% ~A~%~%" (first sub2))
@ -190,6 +214,8 @@
(terpri)
nil)
(defun qapropos* (name &optional class offset)
(%qapropos (%string-or-nil name) (%to-qobject class) offset))
(defun qapropos* (name &optional qobject/name offset)
"args: (name &optional qobject/name)
Similar to QAPROPOS, returning the results as nested list."
(%qapropos (%string-or-nil name) (%to-qobject qobject/name) offset))