"qml-lisp": allow for nested lists, vectors etc. as return values from QML function calls; some revisions;

This commit is contained in:
polos 2017-01-20 20:57:32 +01:00
parent 4cb7bfcfbf
commit f290094824
10 changed files with 119 additions and 25 deletions

View file

@ -12,3 +12,13 @@ For Emacs/Slime, this would be:
eql5 ~/slime/eql-start-swank.lisp tic-tac-toe
NOTES
=====
Try the following:
(qml-set "game" "difficulty" 8) ; one of: 2, 8, 10
Since the logic of the buttons is defined in the QML file, changes to
properties will be reflected immediately by the UI.

View file

@ -1,5 +1,11 @@
// helper functions for convenient QML/EQL5 integration
function checkEval(arg) {
// prepared in Lisp for JS evaluation
if((typeof(arg) == "string") && (arg.substr(0, 3) == "#<>")) {
return eval(arg.substr(3)); }
return arg; }
function fun(name, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) {
var args = [];
if(undefined != arg1) args.push(arg1); {
@ -11,8 +17,8 @@ function fun(name, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) {
if(undefined != arg7) args.push(arg7); {
if(undefined != arg8) args.push(arg8); {
if(undefined != arg9) args.push(arg9); }}}}}}}}
return EQL5.apply(name, args); }
return checkEval(EQL5.apply(name, args)); }
function apply(name, args) {
return EQL5.apply(name, args); }
return checkEval(EQL5.apply(name, args)); }

View file

@ -18,8 +18,6 @@ QVariant Lisp::apply(const QString& function, const QVariantList& arguments) {
eql_fun("qml:qml-apply", QVariant::String,
Q_ARG(QString, function),
Q_ARG(QVariantList, arguments));
if(ret.toString() == "NIL") {
ret = QVariant(); }
return ret; }
QT_END_NAMESPACE

View file

@ -34,13 +34,44 @@
;;; function calls from QML
(defun print-js-readably (object)
"Prints lists, vectors, T, NIL, floats in JS notation, which will be passed to JS 'eval()'."
(if (and (not (stringp object))
(vectorp object))
(print-js-readably (coerce object 'list))
(typecase object
(cons
(write-char #\[)
(do ((list object (rest list)))
((null list) (write-char #\]))
(print-js-readably (first list))
(when (rest list)
(write-char #\,))))
(float
;; cut off Lisp specific notations
(princ (string-right-trim "dl0" (princ-to-string object))))
(t
(cond ((eql 't object)
(princ "true"))
((eql 'nil object)
(princ "false"))
(t
(prin1 object)))))))
(defun print-to-js-string (object)
(with-output-to-string (*standard-output*)
(princ "#<>") ; mark for passing to JS "eval()"
(print-js-readably object)))
(defun qml-apply (function arguments)
"Every 'Lisp.fun()' or 'Lisp.apply()' function call in QML will call this function."
(let ((value (apply (string-to-symbol function)
(let ((object (apply (string-to-symbol function)
arguments)))
(if (stringp value)
value
(princ-to-string value))))
(if (stringp object)
object
(print-to-js-string object))))
;;; utils
(let (root-object)
(defun root-object ()

View file

@ -18,22 +18,30 @@ You can access any QML property from Lisp (needs 'objectName' to be set).
Examples:
(in-package :qml)
;; single object
(qml:qml-get "label" "text")
(qml:qml-set "label" "text" "hi!")
(qml:qml-set "label" "color" "red")
(qml-get "label" "text")
(qml-set "label" "text" "hi!")
(qml-set "label" "color" "red")
;; all objects matching 'objectName'
(qml:qml-get* "label" "text")
(qml:qml-set* "label" "text" "")
(qml-get* "label" "text")
(qml-set* "label" "text" "")
NOTES
=====
Please see also the documentation in "qml/example.qml".
TIP
===
In order to have uniform access to QML objects from both JS and Lisp
functions, it's convenient to set both 'id:' and 'objectName:' to the same
name.
functions, it is convenient to set both 'id:' and 'objectName:' to the
same name.
QML Example:

View file

@ -1,5 +1,11 @@
// helper functions for convenient QML/EQL5 integration
function checkEval(arg) {
// prepared in Lisp for JS evaluation
if((typeof(arg) == "string") && (arg.substr(0, 3) == "#<>")) {
return eval(arg.substr(3)); }
return arg; }
function fun(name, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) {
var args = [];
if(undefined != arg1) args.push(arg1); {
@ -11,8 +17,8 @@ function fun(name, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) {
if(undefined != arg7) args.push(arg7); {
if(undefined != arg8) args.push(arg8); {
if(undefined != arg9) args.push(arg9); }}}}}}}}
return EQL5.apply(name, args); }
return checkEval(EQL5.apply(name, args)); }
function apply(name, args) {
return EQL5.apply(name, args); }
return checkEval(EQL5.apply(name, args)); }

View file

@ -18,8 +18,6 @@ QVariant Lisp::apply(const QString& function, const QVariantList& arguments) {
eql_fun("qml:qml-apply", QVariant::String,
Q_ARG(QString, function),
Q_ARG(QVariantList, arguments));
if(ret.toString() == "NIL") {
ret = QVariant(); }
return ret; }
QT_END_NAMESPACE

View file

@ -34,13 +34,44 @@
;;; function calls from QML
(defun print-js-readably (object)
"Prints lists, vectors, T, NIL, floats in JS notation, which will be passed to JS 'eval()'."
(if (and (not (stringp object))
(vectorp object))
(print-js-readably (coerce object 'list))
(typecase object
(cons
(write-char #\[)
(do ((list object (rest list)))
((null list) (write-char #\]))
(print-js-readably (first list))
(when (rest list)
(write-char #\,))))
(float
;; cut off Lisp specific notations
(princ (string-right-trim "dl0" (princ-to-string object))))
(t
(cond ((eql 't object)
(princ "true"))
((eql 'nil object)
(princ "false"))
(t
(prin1 object)))))))
(defun print-to-js-string (object)
(with-output-to-string (*standard-output*)
(princ "#<>") ; mark for passing to JS "eval()"
(print-js-readably object)))
(defun qml-apply (function arguments)
"Every 'Lisp.fun()' or 'Lisp.apply()' function call in QML will call this function."
(let ((value (apply (string-to-symbol function)
(let ((object (apply (string-to-symbol function)
arguments)))
(if (stringp value)
value
(princ-to-string value))))
(if (stringp object)
object
(print-to-js-string object))))
;;; utils
(let (root-object)
(defun root-object ()

View file

@ -10,7 +10,11 @@ Item {
Component.onCompleted: {
// Please note:
// Lisp.fun() is limited to 9 arguments; use Lisp.apply() for more arguments.
//
// * Lisp.fun() is limited to 9 arguments; use Lisp.apply() for more arguments
//
// * return values can be nested Lisp lists or vectors, which will be converted to
// nested JS arrays: they will be prepared in Lisp and passed to JS eval
// (1) call CL function
console.log(Lisp.fun("format", false, "~R", 123))
@ -22,6 +26,8 @@ Item {
console.log(Lisp.fun("x:join", ["11", "55"], ":"))
// (4) nested list arguments; note Lisp.apply() for any number of arguments
// N.B: don't get fooled by the printed representation of the return value:
// it's a nested JS array internally
console.log(Lisp.apply("list", [[[1, 2, 3], ["a", "b", "c"], 4, 5], 6, [[7, 8], 9]]))
}

View file

@ -226,7 +226,7 @@
(! "name" obj))
((search name "QDate QTime QDateTime QFont QUrl QKeySequence")
(! "toString" obj))
((search name "QPixmap QImage QPicture QIcon QTextCursor QVariant QMargins QWebElement")
((search name "QPixmap QImage QPicture QIcon QBitmap QDate QDateTime QTime QTextCursor QVariant QMargins QSqlQuery QWebElement")
(if (and (not (zerop (qt-object-pointer obj)))
(! "isNull" obj))
(null-qt-object obj)