QWidget :: render () в QImage

У меня есть окно, содержимое которого я хочу экспортировать в файл .png. Я использую его функцию рендеринга для рендеринга на QImage:

QLOG("Image export mode");
const QString refpath = seq_->item(0)->path(0);
QImage refimg(refpath);
const QSize imgsize = refimg.size();
QLOG("image size: " << imgsize);
Q_ASSERT(mapwin_);
mapwin_->resize(imgsize);
// the window doesn't seem to fit the image well the first time around, so do it
// once and disregard the result - no idea why this happens.
mapwin_->showImage(refimg);
QImage winimage(mapwin_->size(), QImage::Format_ARGB32);
emit mapFit();
QPainter painter(&winimage);
emit renderWindow(&painter);

emit message("Exporting images");
emit startProgress(itemIndices_.size());

// iterate over indices of selected images in sequence
for (int i = 0; i < itemIndices_.size(); ++i)
{
    const int itemidx = itemIndices_.at(i);
    Q_ASSERT(itemidx >= 0);
    const SeqItem *item = seq_->item(itemidx);
    Q_ASSERT(item->mapExists());
    QLOG("Item " << i << " (" << itemidx << "): '" << item->filename(0) << "'");
    QLOGINC;

    // iterate over selected displacement components
    for (int ci = 0; ci < cci::VAL_COUNT; ++ci)
    {
        const cci::ValueID id = cci::VAL_ID[ci];
        if (!dcomp_.active(id)) continue;
        const QString label = ValueIDWrapper(id).shortName();
        QLOG("Active value: " << label);

        mapwin_->setValueID(id);
        emit mapIndexUpdate(itemidx);
        emit mapFit();
        emit renderWindow(&painter);

        const QString exportpath = dirPath_ + "/" + item->filename(0).remove(".bmp", Qt::CaseSensitive) + "_" + label + ".png";
        winimage.save(exportpath);
    }

    emit makeProgress();
    QLOGDEC;
}

Я отправил сигнал в поток графического интерфейса пользователя (emit renderwindow), который выполняет следующие действия:

void MapWindow::renderOn(QPainter *image)
{
    render(image, QPoint(), QRegion(), QWidget::DrawChildren);
}

К сожалению, когда я запускаю этот код, я получаю QT ASSERT: [Окно ошибки] [1] после выдачи сигнала. Что может пойти не так, когда я попытаюсь нарисовать QWidget на QImage?

Весь стек вызовов в ASSERT (с этой информацией я пришел к выводу, что это проблема с методом renderOn, поскольку он последний перед исключением):

Qt5Cored.dll!qt_message_fatal(QtMsgType __formal, const QMessageLogContext & context, const QString & message) Line 1571    C++
Qt5Cored.dll!QMessageLogger::fatal(const char * msg, ...) Line 781  C++
Qt5Cored.dll!qt_assert(const char * assertion, const char * file, int line) Line 2967   C++
Qt5Widgetsd.dll!QGraphicsSceneBspTreeIndexPrivate::estimateItems(const QRectF & rect, Qt::SortOrder order, bool onlyTopLevelItems) Line 379 C++
Qt5Widgetsd.dll!QGraphicsSceneBspTreeIndex::estimateTopLevelItems(const QRectF & rect, Qt::SortOrder order) Line 535    C++
Qt5Widgetsd.dll!QGraphicsScenePrivate::drawItems(QPainter * painter, const QTransform * const viewTransform, QRegion * exposedRegion, QWidget * widget) Line 4689   C++
Qt5Widgetsd.dll!QGraphicsView::paintEvent(QPaintEvent * event) Line 3540    C++
Qt5Widgetsd.dll!QWidget::event(QEvent * event) Line 8831    C++
Qt5Widgetsd.dll!QFrame::event(QEvent * e) Line 540  C++
Qt5Widgetsd.dll!QAbstractScrollArea::viewportEvent(QEvent * e) Line 1200    C++
Qt5Widgetsd.dll!QGraphicsView::viewportEvent(QEvent * event) Line 2963  C++
Qt5Widgetsd.dll!QAbstractScrollAreaPrivate::viewportEvent(QEvent * event) Line 102  C++
Qt5Widgetsd.dll!QAbstractScrollAreaFilter::eventFilter(QObject * o, QEvent * e) Line 118    C++
Qt5Cored.dll!QCoreApplicationPrivate::sendThroughObjectEventFilters(QObject * receiver, QEvent * event) Line 1072   C++
Qt5Widgetsd.dll!QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e) Line 3712    C++
Qt5Widgetsd.dll!QApplication::notify(QObject * receiver, QEvent * e) Line 3681  C++
Qt5Cored.dll!QCoreApplication::notifyInternal(QObject * receiver, QEvent * event) Line 965  C++
>Qt5Cored.dll!QCoreApplication::sendSpontaneousEvent(QObject * receiver, QEvent * event) Line 227   C++
Qt5Widgetsd.dll!QWidgetPrivate::sendPaintEvent(const QRegion & toBePainted) Line 5628   C++
Qt5Widgetsd.dll!QWidgetPrivate::drawWidget(QPaintDevice * pdev, const QRegion & rgn, const QPoint & offset, int flags, QPainter * sharedPainter, QWidgetBackingStore * backingStore) Line 5569  C++
Qt5Widgetsd.dll!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice * pdev, const QList<QObject *> & siblings, int index, const QRegion & rgn, const QPoint & offset, int flags, QPainter * sharedPainter, QWidgetBackingStore * backingStore) Line 5761    C++
Qt5Widgetsd.dll!QWidgetPrivate::drawWidget(QPaintDevice * pdev, const QRegion & rgn, const QPoint & offset, int flags, QPainter * sharedPainter, QWidgetBackingStore * backingStore) Line 5619  C++
Qt5Widgetsd.dll!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice * pdev, const QList<QObject *> & siblings, int index, const QRegion & rgn, const QPoint & offset, int flags, QPainter * sharedPainter, QWidgetBackingStore * backingStore) Line 5761    C++
Qt5Widgetsd.dll!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice * pdev, const QList<QObject *> & siblings, int index, const QRegion & rgn, const QPoint & offset, int flags, QPainter * sharedPainter, QWidgetBackingStore * backingStore) Line 5748    C++
Qt5Widgetsd.dll!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice * pdev, const QList<QObject *> & siblings, int index, const QRegion & rgn, const QPoint & offset, int flags, QPainter * sharedPainter, QWidgetBackingStore * backingStore) Line 5748    C++
Qt5Widgetsd.dll!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice * pdev, const QList<QObject *> & siblings, int index, const QRegion & rgn, const QPoint & offset, int flags, QPainter * sharedPainter, QWidgetBackingStore * backingStore) Line 5748    C++
Qt5Widgetsd.dll!QWidgetPrivate::drawWidget(QPaintDevice * pdev, const QRegion & rgn, const QPoint & offset, int flags, QPainter * sharedPainter, QWidgetBackingStore * backingStore) Line 5619  C++
Qt5Widgetsd.dll!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice * pdev, const QList<QObject *> & siblings, int index, const QRegion & rgn, const QPoint & offset, int flags, QPainter * sharedPainter, QWidgetBackingStore * backingStore) Line 5761    C++
Qt5Widgetsd.dll!QWidgetPrivate::drawWidget(QPaintDevice * pdev, const QRegion & rgn, const QPoint & offset, int flags, QPainter * sharedPainter, QWidgetBackingStore * backingStore) Line 5619  C++
Qt5Widgetsd.dll!QWidgetPrivate::render(QPaintDevice * target, const QPoint & targetOffset, const QRegion & sourceRegion, QFlags<enum QWidget::RenderFlag> renderFlags) Line 5705    C++
Qt5Widgetsd.dll!QWidget::render(QPainter * painter, const QPoint & targetOffset, const QRegion & sourceRegion, QFlags<enum QWidget::RenderFlag> renderFlags) Line 5145  C++
Qt5Widgetsd.dll!QWidget::render(QPaintDevice * target, const QPoint & targetOffset, const QRegion & sourceRegion, QFlags<enum QWidget::RenderFlag> renderFlags) Line 5068   C++
ccigui.exe!MapWindow::renderOn(QImage * image) Line 453 C++
ccigui.exe!MapWindow::qt_static_metacall(QObject * _o, QMetaObject::Call _c, int _id, void * * _a) Line 475 C++
Qt5Cored.dll!QMetaCallEvent::placeMetaCall(QObject * object) Line 487   C++
Qt5Cored.dll!QObject::event(QEvent * e) Line 1239   C++
Qt5Widgetsd.dll!QWidget::event(QEvent * event) Line 9105    C++
Qt5Widgetsd.dll!QMdiSubWindow::event(QEvent * event) Line 2917  C++
Qt5Widgetsd.dll!QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e) Line 3716    C++
Qt5Widgetsd.dll!QApplication::notify(QObject * receiver, QEvent * e) Line 3681  C++
Qt5Cored.dll!QCoreApplication::notifyInternal(QObject * receiver, QEvent * event) Line 965  C++
Qt5Cored.dll!QCoreApplication::sendEvent(QObject * receiver, QEvent * event) Line 224   C++
Qt5Cored.dll!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver, int event_type, QThreadData * data) Line 1593    C++
Qt5Cored.dll!QEventDispatcherWin32::sendPostedEvents() Line 1242    C++
qwindowsd.dll!QWindowsGuiEventDispatcher::sendPostedEvents() Line 84    C++
Qt5Cored.dll!qt_internal_proc(HWND__ * hwnd, unsigned int message, unsigned __int64 wp, __int64 lp) Line 416    C++
[External Code] 
Qt5Cored.dll!QEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 818    C++
qwindowsd.dll!QWindowsGuiEventDispatcher::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 74   C++
Qt5Cored.dll!QEventLoop::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 129   C++
Qt5Cored.dll!QEventLoop::exec(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 204    C++
Qt5Widgetsd.dll!QDialog::exec() Line 542    C++
ccigui.exe!Corr2DProject::handle_exportShow(int type, int seqidx) Line 387  C++
ccigui.exe!CCIGui::slot_anal_exportClicked() Line 1324  C++
ccigui.exe!CCIGui::qt_static_metacall(QObject * _o, QMetaObject::Call _c, int _id, void * * _a) Line 464    C++
Qt5Cored.dll!QMetaObject::activate(QObject * sender, int signalOffset, int local_signal_index, void * * argv) Line 3715 C++
Qt5Cored.dll!QMetaObject::activate(QObject * sender, const QMetaObject * m, int local_signal_index, void * * argv) Line 3579    C++
Qt5Widgetsd.dll!QAbstractButton::clicked(bool _t1) Line 304 C++
Qt5Widgetsd.dll!QAbstractButtonPrivate::emitClicked() Line 536  C++
Qt5Widgetsd.dll!QAbstractButtonPrivate::click() Line 528    C++
Qt5Widgetsd.dll!QAbstractButton::mouseReleaseEvent(QMouseEvent * e) Line 1133   C++
Qt5Widgetsd.dll!QWidget::event(QEvent * event) Line 8679    C++
Qt5Widgetsd.dll!QAbstractButton::event(QEvent * e) Line 1090    C++
Qt5Widgetsd.dll!QPushButton::event(QEvent * e) Line 674 C++
Qt5Widgetsd.dll!QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e) Line 3716    C++
Qt5Widgetsd.dll!QApplication::notify(QObject * receiver, QEvent * e) Line 3276  C++
Qt5Cored.dll!QCoreApplication::notifyInternal(QObject * receiver, QEvent * event) Line 965  C++
Qt5Cored.dll!QCoreApplication::sendSpontaneousEvent(QObject * receiver, QEvent * event) Line 227    C++
Qt5Widgetsd.dll!QApplicationPrivate::sendMouseEvent(QWidget * receiver, QMouseEvent * event, QWidget * alienWidget, QWidget * nativeWidget, QWidget * * buttonDown, QPointer<QWidget> & lastMouseReceiver, bool spontaneous) Line 2770  C++
Qt5Widgetsd.dll!QWidgetWindow::handleMouseEvent(QMouseEvent * event) Line 556   C++
Qt5Widgetsd.dll!QWidgetWindow::event(QEvent * event) Line 211   C++
Qt5Widgetsd.dll!QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e) Line 3716    C++
Qt5Widgetsd.dll!QApplication::notify(QObject * receiver, QEvent * e) Line 3160  C++
Qt5Cored.dll!QCoreApplication::notifyInternal(QObject * receiver, QEvent * event) Line 965  C++
Qt5Cored.dll!QCoreApplication::sendSpontaneousEvent(QObject * receiver, QEvent * event) Line 227    C++
Qt5Guid.dll!QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent * e) Line 1792  C++
Qt5Guid.dll!QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent * e) Line 1582    C++
Qt5Guid.dll!QWindowSystemInterface::sendWindowSystemEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 626   C++
qwindowsd.dll!QWindowsGuiEventDispatcher::sendPostedEvents() Line 85    C++
Qt5Cored.dll!qt_internal_proc(HWND__ * hwnd, unsigned int message, unsigned __int64 wp, __int64 lp) Line 416    C++
[External Code] 
Qt5Cored.dll!QEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 818    C++
qwindowsd.dll!QWindowsGuiEventDispatcher::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 74   C++
Qt5Cored.dll!QEventLoop::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 129   C++
Qt5Cored.dll!QEventLoop::exec(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 204    C++
Qt5Cored.dll!QCoreApplication::exec() Line 1229 C++
Qt5Guid.dll!QGuiApplication::exec() Line 1528   C++
Qt5Widgetsd.dll!QApplication::exec() Line 2977  C++
ccigui.exe!main(int argc, char * * argv) Line 41    C++
ccigui.exe!WinMain(HINSTANCE__ * __formal, HINSTANCE__ * __formal, char * __formal, int __formal) Line 113  C++
[External Code] 

person Dario3d    schedule 27.01.2016    source источник
comment
Используйте отладчик и проверьте, откуда приходит ASSERT   -  person Kamil Klimek    schedule 27.01.2016
comment
Добавлен стек вызовов с моим комментарием   -  person Dario3d    schedule 28.01.2016


Ответы (1)


Что может пойти не так, когда я попытаюсь нарисовать QWidget в QImage?

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

Рассмотрите возможность использования другой перегрузки, которая принимает QPaintDevice.

person Thomas    schedule 27.01.2016
comment
Раньше я использовал QImage напрямую, производный от QPaintDevice, но получал тот же эффект. - person Dario3d; 28.01.2016
comment
Ваш код и трассировка стека кажутся несовместимыми. Код использует renderWindow(&painter), но трассировка стека имеет renderOn(QImage * image) - person Thomas; 28.01.2016
comment
Вы находитесь в sendPostedEvents, вы инициировали рендеринг в другом потоке? Если так, то это будет проблемой, особенно. поскольку сигнал содержит объект QObject. - person Thomas; 28.01.2016
comment
Попробуйте создать художника / изображение в MapWindow::renderOn и посмотрите, исчезнет ли ошибка. - person Thomas; 28.01.2016
comment
Мне очень жаль, что я недавно перешел на QImage вместо QPainter, стек вызовов взят из реализации QImage. Что касается потоков: да, он инициализирован в другом потоке, но я попытался отобразить простую метку из MapWindow, это было удачно. Проблема возникла, когда я попытался отобразить пользовательское представление MapWindows (производное от QImageView). Как ни странно, он работает, когда я пытаюсь отобразить его в потоке графического интерфейса. Когда я инициализирую его из потока экспорта, я получаю сообщение об ошибке. Должен ли QImageView быть видимым для рендеринга? Я подумал, что это может быть проблемой (возможно, он неправильно инициализирован?) - person Dario3d; 29.01.2016
comment
Рисование изображения в потоке, отличном от того, в котором оно было создано, может быть проблематичным. - person Thomas; 29.01.2016