CommonQt is a Common Lisp binding to the smoke library for Qt.
Download
Get it from git:
git clone git://gitorious.org/commonqt/commonqt.gitGitorious page: http://gitorious.org/commonqt/commonqt.
Github mirror: https://github.com/stassats/commonqt.
Support
Mailing list: commonqt-devel@common-lisp.net, subscription.Reporting bugs: https://github.com/stassats/commonqt/issues.
Dependencies (C++)
CommonQt needs:
- Qt libraries (tested with 4.8)
-
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
- GNU make and GCC (except on Windows).
- Visual C++ (on Windows).
This needs to be updated.
When downloading Qt windows binaries from Nokia, get the Qt 4.6.2
libraries built for Visual Studio 2008 Express Edition. (Don't use
the binary for MinGW.)
Dependencies (Lisp)
CommonQt is mostly tested with SBCL and Clozure CL. It have been reported to run on Allegro and LispWork in the past. And also can run on ABCL and ECL, albeit slower than desired.
Installation
Quicklisp is the preferred way to install commonqt:
(ql:quickload :qt)
The ASDF system runs make for you automatically to build the C++ wrapper library libcommonqt.so (except on Windows).
Run qmake followed by make yourself for troubleshooting.
(On Windows, run qmake followed by vcbuild.)
Examples
- 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)
Calling instance methods
Use the #_ reader macro to invoke Qt methods easily.
Example:
(#_setGeometry window 100 100 500 355)
(#_show window)
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.
The reader macro expands into a use of the qt:call function.
To enable the reader macro, use (enable-syntax) at the top of your file, while is short for (named-readtables:in-readtable :qt).
Calling static methods
Static methods:
Example:
(#_Qt::blue)
Instantiating Qt classes
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.
Example:
(#_new QPushButton "Quit")
Class names are case-sensitive.
In this case, the macro expands into a use of the qt:new function.
Connecting signals and slots
When connecting signals and slots, C++ programmers would use the macros SLOT and SIGNAL to disambiguate string and signal names from each other. We call these QSLOT and QSIGNAL.
Note that QObject::connect() is a static method on QObject.
(#_connect "QObject"
quit-button (QSIGNAL "clicked()")
application (QSLOT "quit()"))
QApplication
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.
Examples:
(setf qt-user:*application* (qt:make-qapplication))
(setf qt-user:*application* (qt:make-qapplication "-display" ":0"))
Subclassing C++ classes
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 "QObject"
instance (QSIGNAL "clicked()")
*application* (QSLOT "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).
Overriding C++ methods
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)))
In overriding methods, you can call qt:call-next-qmethod to call the C++ method that is being intercepted.
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.
Defining signals and slots
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)
QAPROPOS
There a function qapropos similar in spirit to apropos which looks for Qt classes and methods.
Example:
QT-USER> (qapropos "sliderposition") Method QAbstractSlider.setSliderPosition [660] Method QAbstractSlider.sliderPosition [661] Method QStyle.sliderPositionFromValue [14984] Method QStyle.sliderPositionFromValue [14985] Method QStyleOptionSlider.sliderPosition [15351] Method QStyleOptionSlider.setSliderPosition [15364]
QDESCRIBE for classes
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 [12423]
minimumSizeHint QPushButton.minimumSizeHint [12409]
metaObject QPushButton.metaObject [12399]
menu QPushButton.menu [12415]
[long list trimmed here]
QPushButton QPushButton.QPushButton [12402]
Use (QDESCRIBE "QPushButton" T) to see inherited methods.
Properties:
bool autoDefault
bool default
bool flat
Use (QDESCRIBE "QPushButton" T) to see inherited properties.
QDESCRIBE for methods
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 for objects
qdescribe on Qt objects can show their property values at run time:
QT-USER> (enable-syntax)
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.
How about CLOS bindings?
CommonQt does not provide CLOS bindings. There is a different project called cl-smoke which takes that route.