[PyQt] QGLWidget c++ widget works, SIP binding doesn't

Phil Thompson phil at riverbankcomputing.com
Wed Apr 28 22:08:15 BST 2010


On Wed, 28 Apr 2010 15:13:02 -0400, Josh Knox <jknox at irobot.com> wrote:
> Josh Knox wrote:
>> Hi All,
>>
>> I've got a simple custom QGLWidget that works fine when loaded from 
>> c++. (basically the Sierpinski demo)
>>
>> I've built a SIP binding against it, that I can load from python but 
>> it only ever displays a black area and it appears that the event loop 
>> never starts.
>>
>> My SIP file is trivial:
>>
>>    %Import QtCore/QtCoremod.sip
>>    %Import QtGui/QtGuimod.sip
>>
>>    %Import QtOpenGL/QtOpenGLmod.sip
>>    %Module MyGLTest 0
>>
>>
>>    class MyGLWidget : QGLWidget
>>    {
>>    %TypeHeaderCode
>>    #include "../MyGLTest/include/QtGLTest.hpp"
>>    #include <QGLWidget>
>>    %End
>>
>>    public:
>>             MyGLWidget(QWidget* parent /TransferThis/ = 0, const 
>> QGLWidget*
>>    shareWidget = 0, Qt::WindowFlags f = 0);
>>
>>    };
>>
>> My Python script to show it is also trivial (essentially equivalent to 
>> the C++ code to do the same):
>>
>>    import sys
>>    from PyQt4 import QtGui
>>    from MyGLTest import MyGLWidget
>>
>>    if __name__ == "__main__":
>>
>>        app = QtGui.QApplication(sys.argv)
>>
>>        # test the simple GL widget
>>        myGLWidget = MyGLWidget()
>>        myGLWidget.resize(400, 350)
>>        myGLWidget.setWindowTitle('MyGLWidget')
>>        myGLWidget.show()
>>
>>        app.exec_()
>>
>>
>> This just launches a Qt window with an all black widget area.
>>
>> So, given that the C++ widget works, what could I be missing that's 
>> prevent the SIP binding from running properly?
>>
>> I'm still new at this, so any ideas appreciated!
>>
>>
>> Thanks,
>>
>> Josh
>> _______________________________________________
>> PyQt mailing list    PyQt at riverbankcomputing.com
>> http://www.riverbankcomputing.com/mailman/listinfo/pyqt
> 
> Hmm, after lots of researching and building SIP bindings for various 
> widgets I've discovered some strange behavior.
> 
> It appears that for any QWidget class I make in C++, when loaded from 
> python via SIP bindings, the top-level/parent widget does not receive 
> any events. If I place children on a container QWidget in another C++ 
> class and make bindings for that, then the widget does show up and work 
> properly.
> 
> If, in Python, I create a Qwidget and then place my custom widget on it, 
> it does not work; again receives no events.
> 
> I've reproduced this behavior with multiple custom widgets that I have 
> created. The code below demonstrates this problem.
> The ColorSlider class is my widget. It just paints on the QWidget
directly.
> 
> ColorSliderX is a widget that contains a ColorSlider.
> 
> When created in python, ColorSlider does not update or receive events.  
> When created via ColorSliderX, the child ColorSlider works fine.
> 
> I'm still kinda new at this so I might be missing something really 
> obvious, but I have no idea what. My workaround by nsting the widget in 
> the C++ implementation will get me working for now but it seems really 
> wrong to have to do this.
> 
> Any ideas what I'm missing?
> 
> Thanks,
> 
> Josh
> 
> 
> ============= HEADER =============
> 
> #ifndef QTPY_H
> #define QTPY_H
> 
> #include <QtGui/QtGui>
> #include <QtGui/QPaintEvent>
> #include <QtGui/QResizeEvent>
> #include <QtGui/QWheelEvent>
> #include <QtGui/QWidget>
> 
> // colorful slider widget test class
> class ColorSlider : public QWidget
> {
>     Q_OBJECT
> 
> public:
>     ColorSlider(QWidget *parent=0);
>     virtual ~ColorSlider();
> 
> public:
>     QSize sizeHint()        const { return QSize(100,300); }
>     QSize minimumSizeHint() const { return sizeHint();     }
> 
> public slots:
>     void setValue(int value);
> 
> signals:
>     void valueChanged(int value);
> 
> protected:
>     virtual void paintEvent(QPaintEvent* event);
>     virtual void resizeEvent(QResizeEvent* event);
>     virtual void wheelEvent(QWheelEvent *event);
> 
> private:
>     int m_temp;
>     QTransform m_xform;
>     bool m_mdrag;
> };
> 
> /* This class is a workaround to get the ColorSlider to show up with 
> PyQt. */
> class ColorSliderX : public QWidget
> {
>     Q_OBJECT
> public:
>    
>     ColorSliderX(QWidget *parent=0);
>     virtual ~ColorSliderX();
> 
>     // The child widget
>     ColorSlider *slider;
> 
> };
> 
> #endif // QTPY_H
> 
> 
> 
> 
> ============= CPP =============
> 
> /*  ColorSlider & ColorSliderX implementation.
> #include "qtpy.h"
> #include <iostream>
> 
> ColorSlider::ColorSlider(QWidget *parent)
> :   QWidget(parent),
>     m_temp(50),
>     m_mdrag(false)
> {
>     std::cerr << "ColorSlider::ColorSlider()" << std::endl;
>     show();
> }
> 
> ColorSlider::~ColorSlider()
> {
>     std::cerr << "ColorSlider::~ColorSlider()" << std::endl;
> }
> 
> void ColorSlider::setValue(int value)
> {
>     std::cerr << "ColorSlider::setValue(" << value << ")" << std::endl;
> 
>     if (value != m_temp) {
>         if (value < 0)
>             m_temp = 0;
>         else if (value > 100)
>             m_temp = 100;
>         else
>             m_temp = value;
> 
>         emit valueChanged(m_temp);
>         update();
>     }
> }
> 
> void ColorSlider::paintEvent(QPaintEvent* event)
> {
>     std::cerr << "ColorSlider::paintEvent()" << std::endl;
> 
>     QColor color(0,255,0);
>     if (m_temp > 50) {
>         color.setRedF((m_temp - 50)/50.0);
>         color.setGreenF((100 - m_temp)/50.0);
>     }
>     else if (m_temp < 50) {
>         color.setGreenF(m_temp/50.0);
>         color.setBlueF((50.0 - m_temp)/50.0);
>     }
> 
>     QPainter painter(this);
>     painter.setPen(QPen(Qt::NoPen));
>     painter.setBrush(QBrush(color));
>     painter.setWindow(0, 0, 100, 100);
>     painter.translate(0, 100);
>     painter.scale(1, -1);
>     painter.drawRect(0,0,100,m_temp);
> 
>     m_xform = painter.combinedTransform();
> }
> 
> void ColorSlider::resizeEvent(QResizeEvent* event)
> {
>     std::cerr << "ColorSlider::resizeEvent(" << width() << ", " << 
> height() \
>         << ")" << std::endl;
> }
> 
> void ColorSlider::wheelEvent(QWheelEvent *event)
> {
>     if (event->delta()>0 && m_temp<100) {
>         std::cerr << "ColorSlider::wheelEvent(SCROLL_UP)" << std::endl;
>         setValue(m_temp + 1);
>     }
>     else if (event->delta()<0 && m_temp>0) {
>         std::cerr << "ColorSlider::wheelEvent(SCROLL_DOWN)" << std::endl;
>         setValue(m_temp - 1);
>     }
> }
> 
> 
> /* The parent container */
> ColorSliderX::ColorSliderX(QWidget* parent):
>     QWidget(parent), slider(new ColorSlider(this))
> {
>     slider->show();
>     std::cerr << "ColorSliderX::ColorSliderX()" << std::endl;
> }
> 
> ColorSliderX::~ColorSliderX()
> {
>     std::cerr << "ColorSliderX::~ColorSliderX()" << std::endl;
> }
> 
> 
> ============== SIP =============
> %Module QtPy 0
> 
> %Import QtCore/QtCoremod.sip
> %Import QtGui/QtGuimod.sip
> 
> class ColorSlider : QWidget
> {
> %TypeHeaderCode
> #include "../QtTest/QtPy/qtpy.h"
> %End
> 
> public:
>     ColorSlider(QWidget* parent /TransferThis/ = 0);
>     ~ColorSlider();
>    
> public slots:
>     void setValue(int value);
> 
> signals:
>     void valueChanged(int value);
> };
> 
> class ColorSliderX : QWidget
> {
> %TypeHeaderCode
> #include "../QtTest/QtPy/qtpy.h"
> %End
> 
> public:
>     ColorSliderX(QWidget* parent /TransferThis/ = 0);
>     ~ColorSliderX();
>     ColorSlider* slider;
>    
> };

One thing you haven't done (though it might not make a difference in this
case) is to provide %ConvertToSubClassCode.

Phil


More information about the PyQt mailing list