Skip to content

Qyoto custom slots and signals are working

Thursday, 8 June 2006  |  richard dale

The Qyoto C#/mono bindings are getting quite close to being useful. I first started on them about two and a half years ago, and so it's taken forever to get going. But we've just passed a key milestone, which is getting the cannon game tutorial t7 working. That means you can now define custom slots and signals in C#, and connect them to C++ or C# slots and signals.

A couple of months ago Simon Edwards ported the Qt3 bindings to Qt4, and after that it seemed a bit late to bother with Qt3. I got the Qt4 code generation working, and then Arno Rehn added the code to get the custom signals declarations, and transform them into structures suitable for lookup at runtime. More recently Paolo Capriotti has being doing great stuff getting the runtime creation of QMetaObjects working and implementing custom slots. I've just commited the QtDBus classes, and I'm hoping it will be as easy to get that working as it was for QtRuby.

Here is what tutorial t7 looks like. The LCDRange class has both a custom slot and custom signal, and the signal is connected to a C++ signal emitted by QSlider. The classes are now all in a 'Qyoto' namespace, and so the code starts with a 'using Qyoto;' declaration.

using Qyoto;

class LCDRange : QWidget { private QSlider slider;

public LCDRange() : this((QWidget) null) {}

public LCDRange(QWidget parent) : base(parent) {
    QLCDNumber lcd = new QLCDNumber(2);
    lcd.SetSegmentStyle(QLCDNumber.SegmentStyle.Filled);
    
    slider = new QSlider(Qt.Orientation.Horizontal);
    slider.SetRange(0,99);
    slider.SetValue(0);
    
    Connect(slider, SIGNAL("valueChanged(int)"),
            lcd, SLOT("display(int)"));
    Connect(slider, SIGNAL("valueChanged(int)"),
            this, SIGNAL("valueChanged(int)"));
            
    QVBoxLayout layout = new QVBoxLayout();
    layout.AddWidget(lcd);
    layout.AddWidget(slider);
    
    SetLayout(layout);
    
}
public int Value() {
    return slider.Value();
}

[Q_SLOT("setValue(int)")]
public void SetValue(int value) {
    slider.SetValue(value);
}

protected new ILCDRangeSignals Emit() {
    return (ILCDRangeSignals) Q_EMIT;
}

}

public interface ILCDRangeSignals : IQWidgetSignals { [Q_SIGNAL("valueChanged(int)")] void ValueChanged(int newValue); }

class MyWidget : QWidget { public MyWidget(QWidget parent) : base(parent) { QPushButton quit = new QPushButton("Quit"); Connect(quit, SIGNAL("clicked()"), qApp, SLOT("quit()"));

    QGridLayout grid = new QGridLayout();
    LCDRange previousRange = null;
    
    for (int row = 0; row < 3; ++row) {
        for (int column = 0; column < 3; ++column) {
            LCDRange lcdRange = new LCDRange();
            grid.AddWidget(lcdRange, row, column);
            if (previousRange != null)
                Connect(lcdRange, SIGNAL("valueChanged(int)"),
                        previousRange, SLOT("setValue(int)"));
            previousRange = lcdRange;
        }
    }
    
    QVBoxLayout layout = new QVBoxLayout();
    layout.AddWidget(quit);
    layout.AddLayout(grid);
    SetLayout(layout);
}
protected new IMyWidgetSignals Emit() {
    return (IMyWidgetSignals) Q_EMIT;
}    
public static void Main(string[] args) {
    new QApplication(args);
    MyWidget main = new MyWidget((QWidget)null);
    main.Show();
    QApplication.Exec();
}

}

public interface IMyWidgetSignals : IQWidgetSignals { }

A slot is defined with a Q_SLOT() attribute like this, with the C++ type signature as a string argument (we hope to make the optional in the future):

[Q_SLOT("setValue(int)")] public void SetValue(int value) { slider.SetValue(value); }

Signals are defined in a similar manner. You need to add an interface to your class, and add Q_SIGNAL attributes to the method signature declarations. Then add an Emit() method which returns the type of the signals interface like this: ... protected new ILCDRangeSignals Emit() { return (ILCDRangeSignals) Q_EMIT; } }

public interface ILCDRangeSignals : IQWidgetSignals { [Q_SIGNAL("valueChanged(int)")] void ValueChanged(int newValue); }

Well that's all for now, the next stop is D-BUS..