D  + 

Qt bindings to D programming language

Eldar Insafutdinov, Tuesday, July 28


















Hello World application in Qt

#include <QApplication>
#include <QPushButton>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QPushButton hello("Hello world!");

    hello.show();
    
    return app.exec();
}


extern "C" wrapper around Qt classes and methods

// qpushbutton_wrap.cpp
#include <QPushButton>
extern "C" { void* QPushButton_new(QWidget *parent) { return new QPushButton(parent); } void QPushButton_delete(QPushButton *button) { delete button; } void QPushButton_show(QPushButton *button) { button->show(); } }


Qt class hierarchy is then recreated in D, calling C wrapper functions

// qpushbutton_wrap.cpp
module qt.gui.QPushButton; class QPushButton { private void* cpp_ptr; this(QWidget parent = null) { cpp_ptr = QPushButton_new(parent.cpp_ptr); } ~this() { QPushButton_delete(cpp_ptr); } void show() { _QPushButton_show(cpp_ptr); } } extern (C): void* QPushButton_new(void* parent); void QPushButton_delete(void *pushButton); void QPushButton_show(void *pushButton);


After repeating the same procedure for QApplication we can finally rewrite our Hello World application in D!

// hello_world.d
import qt.gui.QApplication; import qt.gui.QPushButton; int main(string[] args) { auto app = new QApplication(args); auto hello = new QPushButton("Hello world!"); hello.show(); return app.exec(); }


Virtual functions in QtD. How to override mousePressEvent()?

// QWidget.d

module qt.gui.QWidget;


public class QWidget : QObject, IQPaintDevice
{
    ...
    
    protected void mousePressEvent(QMouseEvent arg__1) {
        qtd_QWidget_mousePressEvent_QMouseEvent(this.cpp_ptr, arg__1.this.cpp_ptr);
    }
}


private extern(C) void  qtd_QWidget_mousePressEvent_QMouseEvent(void* __this_cpp_ptr,
 void* arg__1);


Subclassing QWidget and overriding mousePressEvent()

// qwidget_wrap.cpp

#include <qwidget.h>

class QWidget_QtDShell : public QWidget, public Qtd_QObjectEntity
{
public:
    QWidget_QtDShell(void* d_ptr, QWidget*  parent0, Qt::WindowFlags  f1) { _d_entity = d_ptr; }
    ~QWidget_QtDShell();
    
    void* d_entity() { return _d_entity; }
    
    void mousePressEvent(QMouseEvent*  arg__1);

private:
    void* _d_entity;
};

extern "C" void qtd_QWidget_mousePressEvent_QMouseEvent_dispatch(void* d_entity, void* arg__1);
void QWidget_QtDShell::mousePressEvent(QMouseEvent* arg__1)
{
    qtd_QWidget_mousePressEvent_QMouseEvent_dispatch(this->d_entity(), arg__1);
}

// QWidget::mousePressEvent(QMouseEvent * arg__1)
extern "C" void qtd_QWidget_mousePressEvent_QMouseEvent
(QWidget_QtDShell* __this, QMouseEvent* arg__1)
{
    __this->mousePressEvent(arg__1);
}


Refined QWidget D wrapper.

// QWidget.d

module qt.gui.QWidget;


public class QWidget : QObject, IQPaintDevice
{
    void* cpp_ptr;
    
    public this(QWidget _parent = null) {
        cpp_ptr = qtd_QWidget_QWidget_QWidget_WindowFlags(cast(void*) this, _parent.cpp_ptr);
    }
    
    ...
    
    protected void mousePressEvent(QMouseEvent arg__1) {
        qtd_QWidget_mousePressEvent_QMouseEvent(this.cpp_ptr, arg__1.this.cpp_ptr);
    }
}


private extern(C) void  qtd_QWidget_mousePressEvent_QMouseEvent(void* __this_cpp_ptr,
 void* arg__1);


// Virtual Dispatch functions
private extern(C) void qtd_QWidget_mousePressEvent_QMouseEvent_dispatch(void *d_entity, void* arg__1)
{
    auto d_object = cast(QWidget) d_entity;
    scope arg__1_d_ref = new QMouseEvent(arg__1, true);
    d_object.mousePressEvent(arg__1_d_ref);
}


Signals and slots. Forget about moc!

module SignalsAndSlots;

import qt.core.QObject;


class Sender : QObject
{
    mixin Signal!("valueChanged", int);
}

class Receiver
{
    void foo(int value)
    {
        doChange(value);
    }
    
    void bar()
    {
        doChange();
    }
}

void freeSlot(int value)
{
    doChange();
}

void main()
{
    auto sender = new Sender;
    auto receiver = new Receiver;
        
    sender.valueChanged.connect(&receiver.foo);
    sender.valueChanged.connect(&receiver.bar);
    sender.valueChanged.connect(&freeSlot);
    
    ...
    
    sender.disconnect(&receiver.foo);
    ...
}


Performance in QtD

void AnalogClock::paintEvent(QPaintEvent *)
{
    static const QPoint hourHand[3] = {
        QPoint(7, 8),
        QPoint(-7, 8),
        QPoint(0, -40)
    };
    static const QPoint minuteHand[3] = {
        QPoint(7, 8),
        QPoint(-7, 8),
        QPoint(0, -70)
    };

    QPainter painter(this);
    painter.drawConvexPolygon(hourHand, 3); 
    painter.drawConvexPolygon(minuteHand, 3);
}


QPoint wrapper

module qt.core.QPoint;

class QPoint
{
    private void* cpp_ptr;
    
    this(int x, int y)
    {
        cpp_ptr = QPoint_new(x, y);
    }
    
    ~this()
    {
        QPoint_delete(cpp_ptr);
    }
}


// usage

{
    auto points = new QPoint[3];
    foreach(QPoint point; points)
        point = new QPoint(x, y);
}


Solution - using C++ types natively.

public struct QPoint
{
    int x() // const
    { return xp; }

    int y() // const
    { return yp; }

    bool opEquals(QPoint p)
    { return xp == p.xp && yp == p.yp; }

    QPoint opAdd(QPoint p)
    { return QPoint(this.xp+p.xp, this.yp+p.yp); }

    QPoint opSub(QPoint p)
    { return QPoint(this.xp-p.xp, this.yp-p.yp); }
    
    QPoint opMul(qreal c)
    { return QPoint(qRound(this.xp*c), qRound(this.yp*c)); }

private:
    int xp;
    int yp;
}



class AnalogClock : public QWidget
{
    void paintEvent(QPaintEvent event)
    {
        const QPoint[3] hourHand = [
            QPoint(7, 8),
            QPoint(-7, 8),
            QPoint(0, -40)
        ];
        const QPoint[3] minuteHand = [
            QPoint(7, 8),
            QPoint(-7, 8),
            QPoint(0, -70)
        ];

        scope painter = new QPainter(this);

        painter.drawConvexPolygon(hourHand);
        painter.setBrush(minuteColor);
    }
}


Memory management in QtD

module qt.core.QObject;


public class QObject
{
    new(size_t size, void* p = null)
    {
        if (!p)
        {
            p = malloc(size);
            if (!p)
                assert(false, "Out of memory");
            GC.addRange(p, size);
        }
        return p;
    }

    delete(void* p)
    {
        if(p)
        {
            free(p);
            GC.removeRange(p);
        }
    }
}


It is actually usable!


















Thank you.