Вызов width(), height(), size() или rect() внутри подкласса QWidget заканчивается segfault

У меня проблема с функцией width(), height(), size() или rect() пользователя QWidget; когда он вызывается, он получает segfault. Это Qt 4.7.

Вот заголовок проблемного класса:

class PlotCanvas : public QWidget
{ 
    void paintEvent(QPaintEvent * e);
    uint64_t smallestDiv();
    uint64_t longestLength();
    void drawGrid(QPainter * painter);
    QVector<Plot*> plots;
    int calculateHeight() const;
    int calculateWidth() const;
    uint32_t cursorPosition;
public:
    PlotCanvas(QWidget * parent = 0) : QWidget(parent) 
    {
        setStyleSheet("background-color: black");
        setAutoFillBackground(true);
        setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
        setFocusPolicy(Qt::StrongFocus);
        update();
    }
    void addPlot(Plot * plot);
    int getDivCount();
};

и фактический код сегментации:

int PlotCanvas::getDivCount()
{
   QRect bounds = rect(); //segfaults
   qDebug() << bounds.size().width();
   qDebug() << bounds.size().height();
   return 10;
}

прослеживается в Qt qrect.h:

inline int QRect::width() const
{ return  x2 - x1 + 1; } //this line finally kills it

Кажется, что внутри QWidget что-то не инициализировано должным образом, но я понятия не имею, что именно, потому что, как видите, я не слишком сильно меняю инициализацию QWidget.

Виджет PlotCanvas находится в подклассе QAbstractScrollArea (который еще не реализован, так что это просто QAbstractScrollArea:

class PlotCanvasScrollArea : public QAbstractScrollArea
{
public:
    PlotCanvasScrollArea(QWidget * parent = 0) : QAbstractScrollArea(parent) 
    {

    }
};

Мне просто нужно определить размер области рисования QWidget, потому что я рисую на диаграмме перехода, и мне нужно знать, сколько делений времени будет соответствовать текущему размеру виджета, потому что, очевидно, размер окна приложения можно изменить.

РЕДАКТИРОВАТЬ:

Код, в котором строится PlotCanvas:

Board(QString & name) : name(name)
{
    hbox = new QHBoxLayout();
    commonKnobGroup = new QGroupBox("Common settings:");
    commonKnobGroup->setMinimumWidth(190);
    commonKnobGroup->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum));
    commonKnobs = new CommonKnobs(commonKnobGroup);
    hsplitter = new QSplitter();
    plotGroupBox = new QGroupBox("Measurement plots:", hsplitter);
    settingsGroupBox = new QGroupBox("Plot settings:", hsplitter);
    plotSplitter = new QSplitter();
    plotHbox = new QHBoxLayout();
    plotHbox->addWidget(plotSplitter);
    plotGroupBox->setLayout(plotHbox);
    settingsVLayout = new QVBoxLayout();
    plotNamesColumn = new QListWidget(plotSplitter);
    scrollArea = new PlotCanvasScrollArea(plotSplitter);
    //scrollArea->setWidgetResizable(true);
    plotsCanvas = new PlotCanvas();
    scrollArea->setViewport(plotsCanvas);
    scrollArea->setBackgroundRole(QPalette::Dark);
    specializedKnobGroup = new QGroupBox("Probe settings:");
    plotSettings = new QStackedWidget(specializedKnobGroup);
    settingsVLayout->addWidget(commonKnobGroup);
    settingsVLayout->addWidget(specializedKnobGroup);
    settingsGroupBox->setLayout(settingsVLayout);
    hbox->addWidget(hsplitter);
    setLayout(hbox);
    connect(plotNamesColumn, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)), this,
            SLOT(changePageNames(QListWidgetItem*, QListWidgetItem*)));
    connect(commonKnobs, SIGNAL(scaleUpdated()), plotsCanvas, SLOT(update()));

};

EDIT2:
Полная трассировка стека:

#0  0x000000010000b9ac in QWidget::rect() const at /Volumes/Data/shinji/QtSDK/Desktop/Qt/474/gcc/include/QtGui/qwidget.h:1007
#1  0x000000010000b454 in PlotCanvas::getDivCount() ()
#2  0x000000010000ab39 in Board::getDivCount() ()
#3  0x0000000100009fbc in CommonKnobs::updateScaleByBeginEnd() ()
#4  0x00000001000015b4 in CommonKnobs::qt_metacall(QMetaObject::Call, int, void**) ()
#5  0x0000000100d09b66 in QMetaObject::activate(QObject*, QMetaObject const*, int, void**) ()
#6  0x000000010070df9d in QSpinBox::valueChanged(int) ()
#7  0x00000001004d9684 in QSpinBoxPrivate::emitSignals(EmitPolicy, QVariant const&) ()
#8  0x000000010043b321 in QAbstractSpinBoxPrivate::setValue(QVariant const&, EmitPolicy, bool) ()
#9  0x000000010043c11a in QAbstractSpinBoxPrivate::_q_editorTextChanged(QString const&) ()
#10 0x000000010043cca7 in QAbstractSpinBox::qt_metacall(QMetaObject::Call, int, void**) ()
#11 0x000000010070e435 in QSpinBox::qt_metacall(QMetaObject::Call, int, void**) ()
#12 0x0000000100d09b66 in QMetaObject::activate(QObject*, QMetaObject const*, int, void**) ()
#13 0x000000010048a1e6 in QLineEdit::textChanged(QString const&) ()
#14 0x000000010048d23a in QLineEdit::qt_metacall(QMetaObject::Call, int, void**) ()
#15 0x0000000100d09b66 in QMetaObject::activate(QObject*, QMetaObject const*, int, void**) ()
#16 0x000000010070c9f9 in QLineControl::textChanged(QString const&) ()
#17 0x0000000100494337 in QLineControl::finishChange(int, bool, bool) ()
#18 0x0000000100496664 in QLineControl::processKeyEvent(QKeyEvent*) ()
#19 0x000000010048b8eb in QLineEdit::keyPressEvent(QKeyEvent*) ()
#20 0x00000001001056bd in QWidget::event(QEvent*) ()
#21 0x000000010048c18b in QLineEdit::event(QEvent*) ()
#22 0x000000010043d9f0 in QAbstractSpinBox::keyPressEvent(QKeyEvent*) ()
#23 0x00000001001056bd in QWidget::event(QEvent*) ()
#24 0x000000010043bb1b in QAbstractSpinBox::event(QEvent*) ()
#25 0x00000001004d7be5 in QSpinBox::event(QEvent*) ()
#26 0x00000001000a9e8d in QApplicationPrivate::notify_helper(QObject*, QEvent*) ()
#27 0x00000001000b198b in QApplication::notify(QObject*, QEvent*) ()
#28 0x0000000100d0321c in QCoreApplication::notifyInternal(QObject*, QEvent*) ()
#29 0x00000001000a9f2c in qt_sendSpontaneousEvent(QObject*, QEvent*) ()
#30 0x0000000100125cc1 in QKeyMapper::sendKeyEvent(QWidget*, bool, QEvent::Type, int, QFlags<Qt::KeyboardModifier>, QString const&, bool, int, unsigned int, unsigned int, unsigned int, bool*) ()
#31 0x0000000100126b43 in QKeyMapperPrivate::translateKeyEvent(QWidget*, OpaqueEventHandlerCallRef*, OpaqueEventRef*, void*, bool) ()
#32 0x000000010006072e in qt_dispatchKeyEvent(void*, QWidget*) ()
#33 0x000000010005478b in -[QCocoaView keyDown:] ()
#34 0x00007fff815a90c7 in -[NSWindow sendEvent:] ()
#35 0x0000000100059891 in -[QCocoaWindow sendEvent:] ()
#36 0x00007fff814ddafa in -[NSApplication sendEvent:] ()
#37 0x000000010005cf0a in -[QNSApplication sendEvent:] ()
#38 0x00007fff814746de in -[NSApplication run] ()
#39 0x0000000100066c04 in QEventDispatcherMac::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) ()
#40 0x0000000100def774 in QEventLoop::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) ()
#41 0x0000000100defa94 in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) ()
#42 0x0000000100df10bc in QCoreApplication::exec() ()
#43 0x0000000100001cf6 in main ()

person Bruno Kremel    schedule 06.07.2012    source источник
comment
rect является функцией-членом QWidget: может произойти сбой, если вы вызовете ее до того, как объект будет полностью построен. Когда вы пытаетесь вызвать функцию?   -  person tmpearce    schedule 07.07.2012
comment
Это после создания объекта PlotCanvas, на самом деле он вызывается внутри общедоступной функции-члена, которая вызывается извне объекта PlotCanvas. Также можно отметить отсутствие вызова getDivCount() в конструкторе.   -  person Bruno Kremel    schedule 07.07.2012
comment
Можете ли вы показать код, где вы создаете объект, а также где вы вызываете функцию? Вы говорите, что он находится внутри другого класса, но конструктор этого класса не инициализирует его...   -  person tmpearce    schedule 07.07.2012
comment
Я добавил код в исходный пост. Оба инициализируются в классе Board. Он находится как область просмотра QAbstractScrollArea.   -  person Bruno Kremel    schedule 07.07.2012
comment
Хорошо... но вы так и не показали, где вызывается getDivCount.   -  person tmpearce    schedule 07.07.2012
comment
Он вызывается классом Board с помощью метода getDivCount(). Метод getDivCount() этой платы вызывается другим классом при изменении значения счетчика. Но я думаю, что это не имеет большого значения, как это называется.   -  person Bruno Kremel    schedule 07.07.2012
comment
Из трассировки стека вы должны были показать нам код функций Board::getDivCount() и CommonKnobs::updateScaleByBeginEnd().   -  person alexisdm    schedule 07.07.2012


Ответы (4)


Посмотрите на трассировку стека краха — ведет ли она куда-нибудь в конструктор? Моя первая мысль заключается в том, что я не уверен, что вам следует вызывать update внутри конструктора - я мог ошибаться, но мне это кажется неправильным. Если обновление должно быть вызвано внутри конструктора QWidget, то его вызовет базовый конструктор (что сделает ваш вызов излишним), а если не должно, то мне совершенно не ясно, что произойдет.

person Michael Kohne    schedule 07.07.2012
comment
Это не так, потому что виджет отображается правильно, он вылетает после выполнения некоторых действий с графическим интерфейсом, которые вызывают метод getDivCount(). Но для спокойствия я его закомментировал, тем не менее он все еще segfaults. - person Bruno Kremel; 07.07.2012

Ваш scrollArea создается как дочерний элемент plotSplitter, но он никогда не добавляется в сплиттер с помощью addWidget(), кроме того, plotSplitter не имеет родителя, что означает, что это будет собственное окно. Я предполагаю, что проблема, вероятно, возникает из-за того, что вы устанавливаете область просмотра scrollArea для plotsCanvas, который является еще одним QWidget без родителя и, следовательно, также имеет собственное окно.

Попробуйте что-то вроде этого и работайте оттуда:

Board(QString & name) : name(name)
{
    hbox = new QHBoxLayout();
    commonKnobGroup = new QGroupBox("Common settings:");
    commonKnobGroup->setMinimumWidth(190);
    commonKnobGroup->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum));
    commonKnobs = new CommonKnobs(commonKnobGroup);
    hsplitter = new QSplitter();
    plotGroupBox = new QGroupBox("Measurement plots:", hsplitter);
    settingsGroupBox = new QGroupBox("Plot settings:", hsplitter);
    plotSplitter = new QSplitter(this);
    plotHbox = new QHBoxLayout();
    plotHbox->addWidget(plotSplitter);
    plotGroupBox->setLayout(plotHbox);
    settingsVLayout = new QVBoxLayout();
    plotNamesColumn = new QListWidget(plotSplitter);
    scrollArea = new PlotCanvasScrollArea(plotSplitter);
    plotSplitter->addWidget(plotNamesColumn);
    plotSplitter->addWidget(scrollArea);
    //scrollArea->setWidgetResizable(true);
    plotsCanvas = new PlotCanvas(this);
    scrollArea->setViewport(plotsCanvas);
    scrollArea->setBackgroundRole(QPalette::Dark);
    specializedKnobGroup = new QGroupBox("Probe settings:");
    plotSettings = new QStackedWidget(specializedKnobGroup);
    settingsVLayout->addWidget(commonKnobGroup);
    settingsVLayout->addWidget(specializedKnobGroup);
    settingsGroupBox->setLayout(settingsVLayout);
    hbox->addWidget(hsplitter);
    setLayout(hbox);
    connect(plotNamesColumn, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)), this,
            SLOT(changePageNames(QListWidgetItem*, QListWidgetItem*)));
    connect(commonKnobs, SIGNAL(scaleUpdated()), plotsCanvas, SLOT(update()));

};
person syplex    schedule 07.07.2012
comment
Он думал об этом, но я не могу представить, что он когда-либо изменит его, потому что визуальный GUI выглядит одинаково - поэтому иерархия виджетов должна быть одинаковой. Но спасибо, что указали на это, я думал, что просто сделать что-то родителем равно вызову addWidget для этого родителя и наоборот. Я пробовал этот код, но результат тот же :(.. Я начинаю думать, что я сломал Qt или что-то в этом роде... - person Bruno Kremel; 07.07.2012
comment
Вы также пытались добавить родителя в hsplitter? Например, hsplitter = new QSplitter(this); Это путь, по которому я бы пошел, потому что единственная причина, по которой я могу придумать этот segfault, заключается в том, что rect() по какой-то причине возвращает ноль, а getDivCount() выполняет деление на ноль. Таким образом, нулевой размер может быть связан с вашими макетами, родителями виджетов и т. д. - person syplex; 07.07.2012
comment
Еще две вещи, которые нужно проверить: 1) Вы смешиваете библиотеки отладки и выпуска? Это может привести к очень странным ошибкам. 2) Ищите переполнение буфера в другом месте кода. Возможно, ошибка где-то в другом месте, и этот вызов просто попал в неправильную точку в памяти. - person tmpearce; 07.07.2012
comment
Как проверить смешение библиотек? Я использую Xcode с настроенным путем поиска библиотеки для QtSDK/Desktop/Qt/474/gcc/include и QtSDK/Desktop/Qt/474/gcc/lib/**, и я добавил фреймворки QtGui.framework и QtCore.framework в проект framweroks, поэтому я действительно не контролирую, какие библиотеки используются. - person Bruno Kremel; 07.07.2012
comment
и я откопал это в документации: Во-первых, мы создаем нужные нам виджеты в макете. Затем мы создаем объект QHBoxLayout и добавляем виджеты в макет. Наконец, мы вызываем QWidget::setLayout() для установки объекта QHBoxLayout в виджет. В этот момент виджеты в макете перестраиваются так, чтобы окно было их родителем. поэтому я понимаю, что когда я добавляю виджет в макет, мне не нужно указывать там родителя, пока я устанавливаю макет с помощью setLayout()... - person Bruno Kremel; 07.07.2012

Попробуйте использовать виджет geometry вместо rect. Также будьте осторожны с конструкторами копирования. Если вы используете переменные-члены, вам нужно будет использовать setGeometry или setRect вместо конструкторов копирования.

См. документацию по геометрии окна Qt.

person phyatt    schedule 07.07.2012

Я разобрался, он использовал parent() в качестве указателя на Board (приведенный с помощью scatic_cast, я думал, что приведение к неправильному объекту с помощью static_cast убьет его в этой точке, но нет) из PlotCanvas и другого класса, который я использую, когда на самом деле parent() ссылается на PlotCanvasScrollArea в случае PlotCanvas и QGroupBox в случае другого класса.
Странно, что отладчик показал, что я на самом деле вскочил в код Board, даже подумал, что адрес вызываемого объекта был явно неправильным, и что даже больше странно, что он вылетал в коде PlotCanvas не в ориджине (код Board).
Но теперь все в порядке. Спасибо вам, ребята :)

person Bruno Kremel    schedule 07.07.2012