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

Josh Knox jknox at irobot.com
Wed Apr 28 23:00:21 BST 2010


Phil Thompson wrote:
> 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
>   
I hadn't seen %ConvertToSubClassCode come up in any of the examples I 
looked at. After reading up on it in the docs, I'm not sure whether I 
need it or not.  I'm having this issue with the very simplest QWidget 
subclasses I've created as well.


More information about the PyQt mailing list