[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