QPainter :: drawImage()在Qt 5.10

时间:2018-04-19 21:19:07

标签: qt qpainter

我的应用程序将3D渲染绘制到离线QImage(由于历史原因没有OpenGL),然后在这样的小部件中绘制QImage

void Render3dModel::paintEvent(QPaintEvent *event) {
    // Update model and render new image
    m_model.calculate();
    QImage image = m_model.getImage();

    // Draw image in widget
    QPainter painter(this);
    painter.drawImage(QPointF(0, 0), image);
}

使用QWidget :: update()基于鼠标移动重新绘制图像:

void Render3dModel::mouseMoveEvent(QMouseEvent *event) {
    m_model.rotate(event);
    update();
}

这种方法在Qt 5.6和Qt 5.9中运行快速而完美。但是,在Qt 5.10中重建完全相同的代码会导致奇怪的故障。两个框架的部分同时可见。这不是暂时的效果(例如延迟绘制),因为如果我停止移动鼠标,它实际上会保持显示两个帧的部分。下面是一个示例(为了清晰起见,使用交替的灰色和白色背景用于偶数/奇数帧):

tearing

我尝试在image.copy()铃声缓冲区中使用QImage创建深层副本,看看这是否是QImage的破坏/重用与某些延迟/延迟绘画之间的某种竞争条件,但这没有用。

显然QPainter的机制在Qt 5.9和Qt 5.10之间发生了变化,但我无法在文档中找到任何线索。

请咨询

更新

在玩了几个窗口大小之后,我得出的结论是,故障在某种程度上对应于左上角对齐的相同大小的矩形与" S" "截面尺寸"标签。但是,正如您在屏幕截图中看到的那样,具有白色背景的框架将锥体的一部分绘制在正确的位置。所以图像不是移位,而是以某种方式剪切

我尝试过添加

painter.setClipping(false);
painter.setClipRect(QRect(0, 0, size().width(), size().height()));

但这没有效果。我的Render3dModel偶尔会从其父母那里得到paintEvent吗?

更新2

在Kuba Ober的最小示例中添加mouseMoveEvent处理程序,在MacBook(OS X 10.12.6,Intel HD Graphics 515)上重现问题:

Reproduced glitch

这可能是Qt 5.10中的回归,因为我无法在Qt 5.9中重现这一点吗?我应该提交错误报告吗?

更新3

在OS X的Qt 5.10中确实出现了回归。提交的错误报告https://bugreports.qt.io/browse/QTBUG-67998

3 个答案:

答案 0 :(得分:1)

我测试了@KubaOber提供的最小示例,按照OP的建议添加了 mouseMoveEvent()处理程序,但我无法在我的平台上重现该问题( Qt 5.10。 1 - Linux )。

在我看来,它可能是一个平台特定的问题。 Qt 5.9和Qt 5.10之间的 cocoa 平台插件存在很多差异,特别是关于 QCocoaBackingStore 及其flush()方法(changelog here)。

如果最小的完整示例显示问题,我建议将其报告给https://bugreports.qt.io的Qt错误跟踪器。

答案 1 :(得分:0)

单线程代码中不存在竞争条件。没有延迟/延迟绘画这样的东西。到paintEvent返回时,该小部件的绘制就完成了。

你看到的就是你得到的。 getImage()方法可能会返回垃圾 - 也许是一个尺寸不足的图像 - 然后你画出那个垃圾。用简单的交替填充替换getImage(),您将看到它正常工作:

screenshot of the example code

// https://github.com/KubaO/stackoverflown/tree/master/questions/glitchy-paint-49930405
#include <QtWidgets>
#include <array>

class Render3dModel : public QWidget {
   struct Model {
      mutable int counter = {};
      QSize size;
      QFont font{"Helvetica", 48};
      QImage getImage() const {
         static auto const format = QPixmap{1,1}.toImage().format();
         QImage image{size, format};
         image.fill((counter & 1) ? Qt::blue : Qt::yellow);
         QPainter p(&image);
         p.setFont(font);
         p.setPen((counter & 1) ? Qt::yellow : Qt::blue);
         p.drawText(image.rect(), QString::number(counter));
         counter++;
         return image;
      }
   } m_model;
   void paintEvent(QPaintEvent *) override {
      m_model.size = size();
      auto image = m_model.getImage();
      QPainter{this}.drawImage(QPoint{0, 0}, image);
   }
};
    
int main(int argc, char **argv) {
   QApplication app{argc, argv};
   QWidget win;
   QVBoxLayout topLayout{&win};
   QTabWidget tabs;
   topLayout.addWidget(&tabs);
   // Tabs
   for (auto text : { "Shape", "Dimensions", "Layout"}) tabs.addTab(new QWidget, text);
   tabs.setCurrentIndex(1);
   QHBoxLayout tabLayout{tabs.currentWidget()};
   QGroupBox dims{"Section Dimensions"}, model{"3D Model"};
   QGridLayout dimsLayout{&dims}, modelLayout{&model};
   for (auto w : {&dims, &model}) tabLayout.addWidget(w);
   // Section Dimensions
   for (auto text : {"Diameter 1", "Diameter 2", "Length"}) {
      auto row = dimsLayout.rowCount();
      std::array<QWidget*, 3> widgets{{new QLabel{text}, new QDoubleSpinBox, new QLabel{"inch"}}};
      for (auto *w : widgets)
         dimsLayout.addWidget(w, row, dimsLayout.count() % widgets.size());
   }
   tabLayout.setAlignment(&dims, Qt::AlignLeft | Qt::AlignTop);
   dims.setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
   // Model
   Render3dModel render;
   modelLayout.addWidget(&render);
   model.setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
   model.setMinimumSize(250, 250);
   win.show();
   return app.exec();
}

答案 2 :(得分:0)

这是你的错误:

void Render3dModel::paintEvent(QPaintEvent *event) {
    // Update model and render new image
    m_model.calculate();
    ...
}

您不应将模型更新绑定到paint事件。 paint事件应该绘制模型的当前状态,并且您可以获得多个后续和/或部分绘制事件,例如,露出窗户的一部分。

您在应用程序中看到的是部分公开事件,在这种情况下是Qt中的错误的结果,但通常您可能会收到部分公开事件并应该准备好处理它们。