From 349b1e4d5fe880c8085c5b705de3fe223e5f1c5e Mon Sep 17 00:00:00 2001 From: polos Date: Fri, 5 Feb 2021 17:39:56 +0100 Subject: [PATCH] add example '9999': draw in JS canvas, calculate in Lisp --- examples/X-extras/9999/9999.jpg | Bin 0 -> 27866 bytes examples/X-extras/9999/README.txt | 3 + examples/X-extras/9999/lisp/main.lisp | 54 +++++ examples/X-extras/9999/lisp/qml-lisp.lisp | 233 ++++++++++++++++++++++ examples/X-extras/9999/qml/main.qml | 49 +++++ examples/X-extras/9999/run.lisp | 15 ++ 6 files changed, 354 insertions(+) create mode 100644 examples/X-extras/9999/9999.jpg create mode 100644 examples/X-extras/9999/README.txt create mode 100644 examples/X-extras/9999/lisp/main.lisp create mode 100644 examples/X-extras/9999/lisp/qml-lisp.lisp create mode 100644 examples/X-extras/9999/qml/main.qml create mode 100644 examples/X-extras/9999/run.lisp diff --git a/examples/X-extras/9999/9999.jpg b/examples/X-extras/9999/9999.jpg new file mode 100644 index 0000000000000000000000000000000000000000..59c3b2b75b869a514137e9db222e8ddf3dfb6fb7 GIT binary patch literal 27866 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 literal 0 HcmV?d00001 diff --git a/examples/X-extras/9999/README.txt b/examples/X-extras/9999/README.txt new file mode 100644 index 0000000..6d1c5df --- /dev/null +++ b/examples/X-extras/9999/README.txt @@ -0,0 +1,3 @@ +simple canvas example: draw in JS, calculate in Lisp + +see also: https://en.wikipedia.org/wiki/Cistercian_numerals diff --git a/examples/X-extras/9999/lisp/main.lisp b/examples/X-extras/9999/lisp/main.lisp new file mode 100644 index 0000000..72211d7 --- /dev/null +++ b/examples/X-extras/9999/lisp/main.lisp @@ -0,0 +1,54 @@ +;;; +;;; 1 +;;; ------- +;;; 5 \ / | +;;; X | 2 +;;; 4 / \ | +;;; ------- +;;; 3 + +(in-package :eql-user) + +(use-package :qml) + +(defvar *number* 0) +(defvar *canvas* "canvas") + +(defun draw-line (x1 y1 x2 y2) + (js *canvas* "drawLine(~D, ~D, ~D, ~D)" + x1 y1 x2 y2)) + +(defun draw-number (number) + (setf *number* number) + (q! |requestPaint| *canvas*)) + +(defun paint () + (draw-line 0 -150 0 150) + (let ((dy -50) + (dig 1)) + (labels ((line (x1 y1 x2 y2) + (when (find dig '(2 4)) + (setf x1 (- x1) + x2 (- x2))) + (when (>= dig 3) + (setf y1 (- y1) + y2 (- y2) + dy 50)) + (draw-line (* 100 x1) (+ dy (* 100 y1)) + (* 100 x2) (+ dy (* 100 y2)))) + (draw (n) + (case n + (1 (line 0 -1 1 -1)) + (2 (line 0 0 1 0)) + (3 (line 0 -1 1 0)) + (4 (line 0 0 1 -1)) + (5 (draw 1) (draw 4)) + (6 (line 1 -1 1 0)) + (7 (draw 1) (draw 6)) + (8 (draw 2) (draw 6)) + (9 (draw 1) (draw 8))))) + (let ((num *number*)) + (x:while (plusp num) + (draw (mod num 10)) + (setf num (floor (/ num 10))) + (incf dig)))))) diff --git a/examples/X-extras/9999/lisp/qml-lisp.lisp b/examples/X-extras/9999/lisp/qml-lisp.lisp new file mode 100644 index 0000000..58c1e29 --- /dev/null +++ b/examples/X-extras/9999/lisp/qml-lisp.lisp @@ -0,0 +1,233 @@ +;;; +;;; * enables QML to call Lisp functions +;;; * allows to get/set any QML property from Lisp (needs 'objectName' to be set) +;;; * allows to call QML methods from Lisp (needs 'objectName' to be set) +;;; * allows to evaluate JS code from Lisp (needs 'objectName' to be set) +;;; + +(defpackage :qml-lisp + (:use :common-lisp :eql) + (:nicknames :qml) + (:export + #:*quick-view* + #:*caller* + #:children + #:file-to-url + #:find-quick-item + #:ini-quick-view + #:js + #:js-arg + #:qml-call + #:qml-get + #:qml-set + #:qml-set-all + #:q! + #:q< + #:q> + #:q>* + #:paint + #:scale + #:reload + #:root-context + #:root-item)) + +(provide :qml-lisp) + +(in-package :qml-lisp) + +(defvar *quick-view* nil) +(defvar *caller* nil) + +(defun string-to-symbol (name) + (let ((upper (string-upcase name)) + (p (position #\: name))) + (if p + (find-symbol (subseq upper (1+ (position #\: name :from-end t))) + (subseq upper 0 p)) + (find-symbol upper)))) + +;;; function calls from QML + +(defun print-js-readably (object) + "Prints (nested) 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 (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()'." + (let* ((*caller* (if (qnull caller) *caller* (qt-object-? caller))) + (object (apply (string-to-symbol function) + arguments))) + (if (stringp object) + object + (print-to-js-string object)))) + +;;; utils + +(defun root-item () + (when *quick-view* + (qrun* (if (= (qt-object-id *quick-view*) (qid "QQmlApplicationEngine")) + (let ((object (first (|rootObjects| *quick-view*)))) + (setf (qt-object-id object) (qid "QObject")) ; unknown to EQL, so resort to QObject + object) + (qt-object-? (|rootObject| *quick-view*)))))) + +(defun root-context () + (when *quick-view* + (|rootContext| *quick-view*))) + +(defun find-quick-item (object-name) + "Finds the first QQuickItem matching OBJECT-NAME." + (let ((root (root-item))) + (unless (qnull root) + (if (string= (|objectName| root) object-name) + (root-item) + (qt-object-? (qfind-child root object-name)))))) + +(defun quick-item (item/name) + (cond ((stringp item/name) + (find-quick-item item/name)) + ((qt-object-p item/name) + item/name) + ((not item/name) + (root-item)))) + +(defun children (item/name) + "Like QML function 'children'." + (qrun* (mapcar 'qt-object-? (|childItems| (quick-item item/name))))) + +(defun scale () + "Returns the scale factor used on high dpi scaled devices (e.g. phones)." + (|effectiveDevicePixelRatio| *quick-view*)) + +(defun reload () + "Force reloading of QML file after changes made to it." + (|clearComponentCache| (|engine| *quick-view*)) + (|setSource| *quick-view* (|source| *quick-view*))) + +(defun file-to-url (file) + "Convert FILE to a QUrl, distinguishing between development and release version." + #+release + (qnew "QUrl(QString)" (x:cc "qrc:/" file)) ; see "Qt Resource System" + #-release + (|fromLocalFile.QUrl| file)) + +;;; call QML methods + +(defun qml-call (item/name method-name &rest arguments) + ;; QFUN+ comes in handy here + (qrun* (apply 'qfun+ (quick-item item/name) method-name arguments))) + +;;; get/set QQmlProperty + +(defun qml-get (item/name property-name) + "Gets QQmlProperty of either ITEM or first object matching NAME." + (qrun* (qlet ((property "QQmlProperty(QObject*,QString)" + (quick-item item/name) + property-name)) + (if (|isValid| property) + (qlet ((variant (|read| property))) + (values (qvariant-value variant) + t)) + (eql::%error-msg "QML-GET" (list item/name property-name)))))) + +(defun qml-set (item/name property-name value &optional update) + "Sets QQmlProperty of either ITEM, or first object matching NAME. Returns T on success. If UPDATE is not NIL and ITEM is a QQuickPaintedItem, |update| will be called on it." + (qrun* (let ((item (quick-item item/name))) + (qlet ((property "QQmlProperty(QObject*,QString)" item property-name)) + (if (|isValid| property) + (let ((type-name (|propertyTypeName| property))) + (qlet ((variant (qvariant-from-value value (if (find #\: type-name) "int" type-name)))) + (prog1 + (|write| property variant) + (when (and update (= (qt-object-id item) (qid "QQuickPaintedItem"))) + (|update| item))))) + (eql::%error-msg "QML-SET" (list item/name property-name value))))))) + +(defun qml-set-all (name property-name value &optional update) + "Sets QQmlProperty of all objects matching NAME." + (assert (stringp name)) + (qrun* (dolist (item (qfind-children (root-item) name)) + (qml-set item property-name value update)))) + +(defmacro q! (method-name item/name &rest arguments) + "Convenience macro for QML-CALL. Use symbol instead of string name." + `(qml-call ,item/name ,(symbol-name method-name) ,@arguments)) + +(defmacro q> (property-name item/name value &optional update) + "Convenience macro for QML-SET. Use symbol instead of string name." + `(qml-set ,item/name ,(symbol-name property-name) ,value ,update)) + +(defmacro q< (property-name item/name) + "Convenience macro for QML-GET. Use symbol instead of string name." + `(qml-get ,item/name ,(symbol-name property-name))) + +(defmacro q>* (property-name item/name value &optional update) + "Convenience macro for QML-SET-ALL. Use symbol instead of string name." + `(qml-set-all ,item/name ,(symbol-name property-name) ,value ,update)) + +;;; JS + +(defun js (item/name js-format-string &rest arguments) + "Evaluates a JS string, with 'this' bound to either ITEM, or first object matching NAME. Arguments are passed through FORMAT." + (qrun* (qlet ((qml-exp "QQmlExpression(QQmlContext*,QObject*,QString)" + (root-context) + (quick-item item/name) + (apply 'format nil js-format-string arguments)) + (variant (|evaluate| qml-exp))) + (qvariant-value variant)))) + +(defun js-arg (object) + "To be used for arguments in function JS." + (with-output-to-string (*standard-output*) + (print-js-readably object))) + +;;; ini + +(defun ini-quick-view (file) + (setf *quick-view* (qnew "QQuickView")) + ;; special settings for mobile, taken from Qt example + (let ((env (ext:getenv "QT_QUICK_CORE_PROFILE"))) + (when (and (stringp env) + (not (zerop (parse-integer env :junk-allowed t)))) + (let ((f (|format| *quick-view*))) + (|setProfile| f |QSurfaceFormat.CoreProfile|) + (|setVersion| f 4 4) + (|setFormat| *quick-view* f)))) + (qconnect (|engine| *quick-view*) "quit()" (qapp) "quit()") + (qnew "QQmlFileSelector(QQmlEngine*,QObject*)" (|engine| *quick-view*) *quick-view*) + (|setSource| *quick-view* (file-to-url file)) + (when (= |QQuickView.Error| (|status| *quick-view*)) + ;; display eventual QML errors + (qmsg (x:join (mapcar '|toString| (|errors| *quick-view*)) + #.(make-string 2 :initial-element #\Newline)))) + (|setResizeMode| *quick-view* |QQuickView.SizeRootObjectToView|) + (let ((platform (|platformName.QGuiApplication|))) + (if (find platform '("qnx" "eglfs") :test 'string=) + (|showFullScreen| *quick-view*) + (|show| *quick-view*)))) + diff --git a/examples/X-extras/9999/qml/main.qml b/examples/X-extras/9999/qml/main.qml new file mode 100644 index 0000000..53f0882 --- /dev/null +++ b/examples/X-extras/9999/qml/main.qml @@ -0,0 +1,49 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import EQL5 1.0 + +Rectangle { + width: 220 + height: 320 + input.height + color: "lavender" + + Canvas { + id: canvas + objectName: "canvas" + width: 220 + height: 320 + + property var painter + + function drawLine(x1, y1, x2, y2) { + painter.moveTo(x1, y1) + painter.lineTo(x2, y2) + } + + onPaint: { + var ctx = getContext("2d") + painter = ctx + ctx.reset() + ctx.strokeStyle = "blue" + ctx.lineWidth = 10 + ctx.translate(110, 160) + + Lisp.call("eql-user:paint") + + ctx.stroke() + } + } + + TextField { + id: input + objectName: "input" + width: parent.width + anchors.bottom: parent.bottom + horizontalAlignment: Qt.AlignHCenter + maximumLength: 4 + text: "0000" + inputMask: "9999" + + onTextChanged: Lisp.call("eql-user:draw-number", Number(text)) + } +} diff --git a/examples/X-extras/9999/run.lisp b/examples/X-extras/9999/run.lisp new file mode 100644 index 0000000..52a39a2 --- /dev/null +++ b/examples/X-extras/9999/run.lisp @@ -0,0 +1,15 @@ +(in-package :eql-user) + +(si::trap-fpe t nil) + +(qrequire :quick) + +(load "lisp/qml-lisp") +(load "lisp/main") + +(use-package :qml) + +(progn + (ini-quick-view "qml/main.qml") + (|setPosition| *quick-view* '(50 50))) +