C++ Qt:绘制高DPI QPixmaps

C++ Qt:绘制高DPI QPixmaps,c++,qt,qpainter,qpixmap,highdpi,C++,Qt,Qpainter,Qpixmap,Highdpi,我写了一份申请书,上面画着两张笑脸: 第一个直接绘制在QWidget上: void DirectFace::paintEvent(QPaintEvent *ev) { QPainter painter(this); paintFace(painter); } 第二个是绘制在QPixmap上的,它依次被布告到小部件: void BufferedFace::paintEvent(QPaintEvent *ev) { QPixmap buffer(width(), heig

我写了一份申请书,上面画着两张笑脸:

第一个直接绘制在QWidget上:

void DirectFace::paintEvent(QPaintEvent *ev)
{
    QPainter painter(this);
    paintFace(painter);
}
第二个是绘制在
QPixmap
上的,它依次被布告到小部件:

void BufferedFace::paintEvent(QPaintEvent *ev)
{
    QPixmap buffer(width(), height());
    buffer.fill(Qt::transparent);
    QPainter painter(&buffer);
    paintFace(painter);

    QPainter p(this);
    p.drawPixmap(ev->rect(), buffer, ev->rect());
}
到目前为止还不错。我想看看我的应用程序在高分辨率屏幕上的样子(我没有),所以我设置了
QT\u SCALE\u FACTOR=2
并运行我的应用程序:

第一个面是尖锐而清晰的,而第二个面是像素化的。这是因为它被绘制到低分辨率的pixmap。因此,我放大了
QPixmap
,并设置了正确的
devicePixelRatio

void BufferedFace::paintEvent(QPaintEvent *ev)
{
    qreal pixelRatio = qApp->devicePixelRatio();
    QPixmap buffer(width() * pixelRatio, height() * pixelRatio);
    buffer.setDevicePixelRatio(pixelRatio);
    buffer.fill(Qt::transparent);
    QPainter painter(&buffer);
    paintFace(painter);

    QPainter p(this);
    p.drawPixmap(ev->rect(), buffer, ev->rect());
}
结果:

第二个面看起来像是以正确的分辨率绘制的,但随后放大。现在我被卡住了。如何绘制
QPixmap
,然后绘制
QPixmap
,使其在视网膜/HiDPI屏幕上正常工作

整个应用程序:

#include <QtWidgets>

class SmilingFace : public QWidget
{
    public:
    SmilingFace(QWidget *parent) : QWidget(parent) {};
    void paintFace(QPainter &painter);
};

class DirectFace : public SmilingFace
{
    public:
    DirectFace(QWidget *parent) : SmilingFace(parent) {}
    void paintEvent(QPaintEvent *ev) override;
};

class BufferedFace : public SmilingFace
{
    public:
    BufferedFace(QWidget *parent) : SmilingFace(parent) {}
    void paintEvent(QPaintEvent *ev) override;
};


void SmilingFace::paintFace(QPainter &painter)
{
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(Qt::NoPen);
    painter.setBrush(QBrush(Qt::lightGray));
    painter.drawEllipse(1, 1, width()-2, height()-2);

    painter.setPen(Qt::white);
    painter.setFont(QFont("", 32));
    painter.drawText(rect(), Qt::AlignHCenter, ";)");
}

void DirectFace::paintEvent(QPaintEvent *ev)
{
    QPainter painter(this);
    paintFace(painter);
}

void BufferedFace::paintEvent(QPaintEvent *ev)
{
    QPixmap buffer(width(), height());
    buffer.fill(Qt::transparent);
    QPainter painter(&buffer);
    paintFace(painter);

    QPainter p(this);
    p.drawPixmap(ev->rect(), buffer, ev->rect());
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget w;
    w.setWindowTitle("HiDPI");

    DirectFace d(&w);
    d.resize(48, 48);
    d.move(16, 16);

    BufferedFace i(&w);
    i.resize(48, 48);
    i.move(16 + 48 + 16, 16);

    w.show();

    return a.exec();
}
#包括
类SmilingFace:publicqwidget
{
公众:
笑脸(QWidget*parent):QWidget(parent){};
空漆面(油漆工和油漆工);
};
类DirectFace:publicssmilingface
{
公众:
DirectFace(QWidget*parent):笑脸(parent){}
无效paintEvent(QPaintEvent*ev)覆盖;
};
类BufferedFace:公共笑脸
{
公众:
BufferedFace(QWidget*parent):笑脸(parent){}
无效paintEvent(QPaintEvent*ev)覆盖;
};
虚空笑脸::漆面(QPainter&painter)
{
painter.setRenderInt(QPainter::抗锯齿);
painter.setPen(Qt::NoPen);
画师:挫折(QBrush(Qt::浅灰色));
painter.drawerlipse(1,1,宽度()-2,高度()-2);
画师:设置笔(Qt::白色);
setFont(QFont(“,32));
painter.drawText(rect(),Qt::AlignHCenter,“;)”;
}
void DirectFace::paintEvent(QPaintEvent*ev)
{
油漆工(本);
漆面(油漆工);
}
void BufferedFace::paintEvent(QPaintEvent*ev)
{
QPixmap缓冲区(宽度(),高度());
buffer.fill(Qt::transparent);
QPainter painter(油漆工和缓冲器);
漆面(油漆工);
油漆工p(本);
p、 drawPixmap(ev->rect(),buffer,ev->rect());
}
int main(int argc,char*argv[])
{
质量保证申请a(argc、argv);
qw;
w、 setWindowTitle(“HiDPI”);
直接面d&w;
d、 调整大小(48,48);
d、 移动(16,16);
缓冲面i&w;
i、 调整大小(48,48);
i、 移动(16+48+16,16);
w、 show();
返回a.exec();
}

如果想要高dpi渲染,还应该为QPaint函数使用QRectF和QPointF参数。在paintFace(…)函数中,调整drawlipse和drawText函数以使用QRect参数而不是QRect。这可能会有帮助

使用qApp->devicePixelRatio()不是一个好主意。有些人使用混合的HighDPI和非HighDPI监视器。由于您在小部件paintEvent(…)函数中,因此可以直接使用QWidget成员函数devicePixelRatioF(),而不是qApp->devicePixelRatio()。这将处理小部件的正确呈现,即使用户以混合分辨率在监视器之间移动小部件

您还应该通过:QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling)在Qt中启用高DPI扩展

下面是完整的解决方案,它可以在HighDPI和非HighDPI屏幕上完美地呈现笑脸,即使小部件在两者之间移动。使用Qt 5.9.2进行测试

#include <QtWidgets>

class SmilingFace : public QWidget
{
public:
    SmilingFace(QWidget *parent) : QWidget(parent) {};
    void paintFace(QPainter &painter);
};

class DirectFace : public SmilingFace
{
public:
    DirectFace(QWidget *parent) : SmilingFace(parent) {}
    void paintEvent(QPaintEvent *ev) override;
};

class BufferedFace : public SmilingFace
{
public:
    BufferedFace(QWidget *parent) : SmilingFace(parent) {}
    void paintEvent(QPaintEvent *ev) override;
};


void SmilingFace::paintFace(QPainter &painter)
{
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(Qt::NoPen);
    painter.setBrush(QBrush(Qt::lightGray));
    painter.drawEllipse(QRectF(1, 1, width() - 2, height() - 2));

    painter.setPen(Qt::white);
    painter.setFont(QFont("", 32));
    painter.drawText(QRectF(0, 0, width(), height()), Qt::AlignHCenter, ";)");
}

void DirectFace::paintEvent(QPaintEvent *ev)
{
    QPainter painter(this);
    paintFace(painter);
}

void BufferedFace::paintEvent(QPaintEvent *ev)
{
    qreal dpr = devicePixelRatioF();
    QPixmap buffer(width() * dpr, height() * dpr);
    buffer.setDevicePixelRatio(dpr);
    buffer.fill(Qt::transparent);
    QPainter painter(&buffer);
    paintFace(painter);

    QPainter p(this);
    p.drawPixmap(ev->rect(), buffer, buffer.rect());
}

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QApplication a(argc, argv);

    QWidget w;
    w.setWindowTitle("HiDPI");

    DirectFace d(&w);
    d.resize(48, 48);
    d.move(16, 16);

    BufferedFace i(&w);
    i.resize(48, 48);
    i.move(16 + 48 + 16, 16);

    w.show();

    return a.exec();
}
#包括
类SmilingFace:publicqwidget
{
公众:
笑脸(QWidget*parent):QWidget(parent){};
空漆面(油漆工和油漆工);
};
类DirectFace:publicssmilingface
{
公众:
DirectFace(QWidget*parent):笑脸(parent){}
无效paintEvent(QPaintEvent*ev)覆盖;
};
类BufferedFace:公共笑脸
{
公众:
BufferedFace(QWidget*parent):笑脸(parent){}
无效paintEvent(QPaintEvent*ev)覆盖;
};
虚空笑脸::漆面(QPainter&painter)
{
painter.setRenderInt(QPainter::抗锯齿);
painter.setPen(Qt::NoPen);
画师:挫折(QBrush(Qt::浅灰色));
painter.drawerlipse(QRectF(1,1,宽度()-2,高度()-2));
画师:设置笔(Qt::白色);
setFont(QFont(“,32));
painter.drawText(QRectF(0,0,width(),height()),Qt::AlignHCenter,“;”);
}
void DirectFace::paintEvent(QPaintEvent*ev)
{
油漆工(本);
漆面(油漆工);
}
void BufferedFace::paintEvent(QPaintEvent*ev)
{
qreal dpr=devicePixelRatioF();
QPixmap缓冲区(宽度()*dpr,高度()*dpr);
缓冲区.setDevicePixelRatio(dpr);
buffer.fill(Qt::transparent);
QPainter painter(油漆工和缓冲器);
漆面(油漆工);
油漆工p(本);
p、 drawPixmap(ev->rect(),buffer,buffer.rect());
}
int main(int argc,char*argv[])
{
QCoreApplication::setAttribute(Qt::AA_enableHighdDiscaling);
质量保证申请a(argc、argv);
qw;
w、 setWindowTitle(“HiDPI”);
直接面d&w;
d、 调整大小(48,48);
d、 移动(16,16);
缓冲面i&w;
i、 调整大小(48,48);
i、 移动(16+48+16,16);
w、 show();
返回a.exec();
}

我认为在没有高DPI显示的情况下测试这一点会让您陷入困境。我不认为将比例因子设置为2是有效的模拟。通常,您所需要做的就是设置DPI属性,因为额外的分辨率是在内部处理的,所以它“通过魔法”为小部件工作。显然,如果你在一张像素地图上画画,你必须自己处理。@NicolasHolthaus:正如我所读到的,这应该足够了:“Qt 5.6支持对遗留应用程序进行跨平台的高DPI扩展,类似于macOS本机进行的扩展。这允许为低DPI屏幕编写的应用程序在高DPI设备上运行不变。此功能是选择性加入的,可以通过以下环境变量启用:(…)QT_SCALE_FACTOR[numeric]”@ddriver:没错,所以我问自己如何处理在pixmap上绘制?