CommonQt is a Common Lisp binding to the smoke library for Qt.
Get it from git:
git clone git://github.com/commonqt/commonqt.gitGithub page: https://github.com/commonqt/commonqt.
Mirror: https://gitlab.common-lisp.net/commonqt/commonqt. email@example.com.
Reporting bugs: https://github.com/commonqt/commonqt/issues.
The easiest way to get all the C++ dependencies is to use a third party project qt-libs, which ensures all the neccesary libraries are present. It is especially useful on Windows, where it doesn't require anything extra to be installed, (ql:quickload :qt-libs) is enough. For more details see the README.
- Qt libraries (tested with 4.8), QT5 is not yet supported.
The smoke library for Qt, provided by the KDE bindings project
(but KDE itself is not required).
Can be built from from git: smokegen first, and then smokeqt.
On Debian testing and Ubuntu:
sudo apt-get install libqt4-dev libsmokeqtgui4-3 libsmokeqt4-devOn Fedora, the following packages are to be installed:
- Qt libraries 4.8.5 for Windows (VS 2010, 235 MB)
- Prebuilt 32-bit binaries: commonqt-libs-20131109.zip (2 MB).
- Both libraries and the /bin/ directory of Qt should be accessible through PATH.
- To build from source, Visual Studio is needed (Visual Studio Express is also suitable).
CommonQt is mostly tested with SBCL and Clozure CL. It have been reported to run on Allegro and LispWorks in the past. And also can run on ABCL and ECL, albeit slower than desired.
Quicklisp is the preferred way to install commonqt:
The ASDF system runs make for you automatically to build the C++ wrapper library libcommonqt.so (except on Windows).
If loading fails during compilation of libcommonqt.so with something like
OPERATION-ERROR while invoking #<COMPILE-OP > on #<CPP->SO "qt" "so" "commonqt">or
OPERATION-ERROR while invoking #<COMPILE-OP > on #<MAKEFILE "qt" "so" "commonqt.pro">run qmake followed by make inside the source directory for troubleshooting.
To find where the source directory is located use
(On Windows, run qmake followed by vcbuild.)
- Lisp translations of the Qt tutorial, step 14: C++ version, Lisp version
- Trivial currency conversion dialog (a typical Mac GUI programming example, I'm told)
The constructor for QApplication requires more FFI magic than you would probably want to write yourself, because it is designed to take the (int* argc, void** argc) arguments from a main function in C.
Use the make-qapplication function provided by Qt instead, which takes string arguments and converts them. QApplication can be instantiated only once, therefore make-qapplication sets *qapplication* variable to the produced instance, and only creates a new one if *qapplication* is NIL.
(make-qapplication "-display" ":0")
(with-main-window (window (#_new QWidget)))is equivalent to doing
(make-qapplication) (with-objects ((window (#_new QWidget))) (#_show window) (#_exec *qapplication*))After closing the window it can be executed again.
Loading additional modules is done using ensure-smoke, (make-qapplication) already loads QtCore and QtGui, anything else has to be loaded explicitly, e.g., (ensure-smoke :qtsvg).
Use the #_ reader macro to invoke Qt methods easily.
(#_setGeometry window 100 100 500 355)
The first argument is the instance to call the method on, the following arguments are passed on to the method.
Method names are case sensitive.
To enable the reader macro, use (named-readtables:in-readtable :qt) at the top of your file.
The reader macro for #_ has a special case: If it is followed by new, it keeps reading to find a class name, then returns a call to a constructor instead.
(#_new QPushButton "Quit")
Class names are case-sensitive.
In this case, the macro expands into a use of the qt:new function.
The function CONNECT is used to connect signals and slots in different ways:
- (connect sender "signal()" receiver "slot()")
- (connect sender "signal(int)" (lambda (int) (print int)))
- (connect sender "signal(int)" receiver (lambda (receiver int) (print int)))
- (connect sender "signal()" receiver (qsignal "signal()"))
(with-signals-blocked (button linedit) ...)
You can make "subclasses" using smoke like this:
(defclass button () () (:metaclass qt-class) (:qt-superclass "QPushButton")) (defmethod initialize-instance :after ((instance button)) ;; Must call the C++ constructor here first: (new instance "label") ;; can call C++ methods on this lisp object afterwards: (connect instance "clicked()" *qapplication* "quit()")
Always specify the metaclass. Specify the qt-superclass only when not subclassing another such Lisp class that already has it. Above we didn't specify a superclass, so it was defaulted to qt:dynamic-object, a subclass of qt:qobject.
Always call the C++ constructor from initialize-instance. Note the use of qt:new with an existing instance that already knows its class (but doesn't have a pointer slot yet).
In a subclass (see the example above), you can override C++ methods like this:
(defclass canvas () () (:metaclass qt-class) (:qt-superclass "QWidget") (:override ("paintEvent" paint-event))) (defmethod paint-event ((this canvas) paint-event) (declare (ignore paint-event)) (let ((painter (#_new QPainter this))) ... paint something ... (#_end painter)))
To call the C++ method in an overridden method, either call-next-qmethod can be used, when the return value is needed, or stop-overriding can be used, which performs a non local transfer of control and never returns, but is implemented more efficiently.
Note that we always intercept all methods of the specified name, ignoring their argument type signature. In this case, there's only one method called paintEvent, so that is safe to do. If there was a second paintEvent method without arguments, you'd have to use &optional or &rest to avoid errors.
In a subclass (see the example above), you can add signals and slots. Here is a real example from tutorial 13:
(defclass cannon-field () (...) (:metaclass qt-class) (:qt-superclass "QWidget") (:slots ("setAngle(int)" (lambda (this newval) (setf (current-angle this) (min (max 5 newval) 70)))) ("setForce(int)" (lambda (this newval) (setf (current-force this) (max 0 newval)))) ("void moveShot()" move-shot) ("void shoot()" shoot) ("void setGameOver()" set-game-over) ("void restartGame()" restart-game)) (:signals ("angleChanged(int)") ("forceChanged(int)") ("void hit()") ("void missed()") ("void canShoot(bool)")))
Note the use of lambda to pre-process values and swap arguments in slots that are meant to just call a CLOS accessor eventually, and the use of lisp function names elsewhere.
Signals don't have a corresponding Lisp function. Emit them like this: (emit-signal object signal-name arguments)
There a function qapropos similar in spirit to apropos which looks for Qt classes and methods.
QT-USER> (qapropos "sliderposition") Method QAbstractSlider::setSliderPosition  Method QAbstractSlider::sliderPosition  Method QStyle::sliderPositionFromValue  Method QStyle::sliderPositionFromValue  Method QStyleOptionSlider::sliderPosition  Method QStyleOptionSlider::setSliderPosition 
There a function qdescribe similar in spirit to describe.
Example for a class name:
QT-USER> (qdescribe "QPushButton") #<QCLASS QPushButton> is a smoke class name: QPushButton flags: VIRTUAL, CONSTRUCTOR Superclasses: QAbstractButton QWidget QObject QPaintDevice Methods: paintEvent# QPushButton::paintEvent  minimumSizeHint QPushButton::minimumSizeHint  metaObject QPushButton::metaObject  menu QPushButton::menu  [long list trimmed here] QPushButton QPushButton::QPushButton  Use (QDESCRIBE "QPushButton" T) to see inherited methods. Properties: bool autoDefault bool default bool flat Use (QDESCRIBE "QPushButton" T) to see inherited properties.
qdescribe can also show methods, if used with a string of the form classname.methodname:
QT-USER> (qdescribe "QPushButton::paintEvent") #<QMETHOD QPushButton::paintEvent> is a smoke method class: #<QCLASS QPushButton> name: paintEvent return type: NIL flags: PROTECTED argument types: #<QTYPE (QPaintEvent*) kind: POINTER, stack: CLASS, class: #<QCLASS QPaintEvent>>
If there are several methods of that name, it shows each method with its arguments.
qdescribe on Qt objects can show their property values at run time:
QT-USER> (qdescribe (#_new QPushButton "test")) #<QPushButton 0x27A654D0> is a smoke object. Use (QDESCRIBE "QPushButton") to see details about its C++ class. Properties: QString objectName "" bool modal NIL Qt::WindowModality windowModality #<QVariant 0x27A65100> bool enabled T QRect geometry #<QRect 0x27443860> QRect frameGeometry #<QRect 0x27A66E20> QRect normalGeometry #<QRect 0x27A63540> int x 0 int y 0 QString text "test" [long list trimmed here]
If there are several methods of that name, it shows each method with its arguments.
Callbacks from threads
Older versions of SBCL had problems with callbacks from foreign threads, e.g. QFileDialog uses them. But since 1.2.9 it works as expected.
Slime on Windows
Running from Slime under Windows doesn't appear to work. Specifically, if the implementation is started as the subprocess of Emacs. To work around the following has to be executed first:
(with-main-window (window (#_new QWidget)) (#_show window) (#_hide window))All subsequent windows do not need this and everything else should work normally.
Another work around, the implementation has to be started from cmd.exe, the swank server created and then Emacs can connect to it. An easy way to achieve that is to find the file start-swank.lisp in the slime directory and do
sbcl --load start-swank.lispAnd then do M-x slime-connect. start-swank.lisp can be edited to change the port and other settings.