我的应用程序将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中重建完全相同的代码会导致奇怪的故障。两个框架的部分同时可见。这不是暂时的效果(例如延迟绘制),因为如果我停止移动鼠标,它实际上会保持显示两个帧的部分。下面是一个示例(为了清晰起见,使用交替的灰色和白色背景用于偶数/奇数帧):
我尝试在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)上重现问题:
这可能是Qt 5.10中的回归,因为我无法在Qt 5.9中重现这一点吗?我应该提交错误报告吗?
更新3
在OS X的Qt 5.10中确实出现了回归。提交的错误报告https://bugreports.qt.io/browse/QTBUG-67998
答案 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()
,您将看到它正常工作:
// 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中的错误的结果,但通常您可能会收到部分公开事件并应该准备好处理它们。