Qt-drawPolyline和drawLine之间的区别

时间:2018-09-26 07:37:09

标签: c++ qt qpainter coordinate-transformation

在我的示例中,我通过以下方式转换了qt painter的坐标系:

 QTransform xform;
 xform.rotate(90);
 xform.scale(1, 1000000000000000);
 xform.translate(0, -std::abs(max));
 if (limit != 0)
     xform.scale(1, std::abs(max)/std::abs(limit));
 painter->setTransform(xform)

之所以如此庞大,是因为我的示例中的数据非常少。然后,我为数据绘制图-它的值大约为1e-14,并且一切都正确绘制。然后,我除了要绘制由两个点指定的图外,还要绘制几条线。

我应用了drawPolyLine函数:

QPointF* line = new QPointF[2];
line[0].setX(0);
line[0].setY(0);
line[1].setX(0);
line[1].setY(std::abs(seismMax));
painter.drawPolyline(line, 2);

其中seismMax大约为1e-14(作为图中所有点)。并画出了线。 然后我应用drawLine(因为我只有两点):

painter.drawLine(QPointF(0, 0), QPointF(0, std::abs(seismMax)));

在这种情况下,未绘制线。我怀疑这两个功能处理的坐标可能不同。就像我用drawLine以原始比例绘制线条时(例如这样):

 painter.drawLine(QPointF(0, 0), QPointF(0, 1000));

线条正确绘制。

1 个答案:

答案 0 :(得分:3)

我提出了MCVE来解决问题。

因此,我使用的缩放比例小得多,无法重现该问题。然后,我使用了OP所述的缩放比例1.0E14。突然,线条图消失了。降低缩放比例,我弄弄说(以我为例)直到1E12都很好用,但是缩放比例较高的行开始消失。玩弄这个时,一位同事走了,并暗示这可能只是浮点问题。我们对此进行了简短讨论并得出结论:

  • 将非常大的数字乘以非常小的数字根本不是问题。它由(整数)乘以尾数并加上指数组成。
  • 用非常小的进行大数的加/减是一个问题,因为两个数字必须用相等的指数(通过移位尾数)进行加/减尾数。

因此,后者可能会发生,从而将非0值擦除为0。

10 14 是二进制的47位数字。 53位尾数接近double的精度。 但是QPainter所使用的渲染引擎可能基于OpenGL,其中float是许多事物的默认设置。 float仅提供23位尾数!

因此,在对此进行了思考之后,我找到了不使用比例缩放系数10 14 进行绘画的充分理由。

为了证明这一点,我制作了一个小样本testQPainterDrawLine.cc

#include <QtWidgets>

class Widget: public QWidget {
  public:
    const double scale; 
  public:
    Widget(double scale, QWidget *pQParent = nullptr):
      QWidget(pQParent),
      scale(scale)
    { }
    virtual ~Widget() = default;
    Widget(const Widget&) = delete;
    Widget& operator=(const Widget&) = delete;
  protected:
    virtual void paintEvent(QPaintEvent *pQEvent) override;
};

void Widget::paintEvent(QPaintEvent*)
{
  const double value = height() / scale;
  QPainter qPainter(this);
  qPainter.fillRect(0, 0, width(), height(), QColor(Qt::white));
  qPainter.drawRect(0, 0, width() - 1, height() - 1);
  QTransform xform;
  xform.scale(1, scale);
  qPainter.setTransform(xform);
  qPainter.setPen(QPen(QColor(Qt::red), 3.0));
  const double xL = 0.333 * width();
  qPainter.drawLine(QPointF(xL, 0.0), QPointF(xL, value));
  qPainter.setPen(QPen(QColor(Qt::blue), 3.0));
  const double xPL = 0.667 * width();
  QPointF qPts[] = { QPointF(xPL, 0.0), QPointF(xPL, value) };
  const int nPts = sizeof qPts / sizeof *qPts; 
  qPainter.drawPolyline(qPts, nPts);
}

int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  QWidget qWin;
  QGridLayout qGrid;
  qGrid.setRowStretch(0, 0); qGrid.setRowStretch(1, 1);
  double scale = 1.0E11;
  for (int i = 0; i < 4; ++i, scale *= 10.0) {
    qGrid.addWidget(
      new QLabel(QString("Scale: %1").arg(scale)),
      0, i);
    qGrid.addWidget(new Widget(scale), 1, i);
  }
  qWin.setLayout(&qGrid);
  qWin.resize(1024, 256);
  qWin.show();
  return app.exec();
}

testQPainterDrawLine.pro

SOURCES = testQPainterDrawLine.cc

QT = widgets

cygwin64中进行了编译和测试:

$ qmake-qt5 testQPainterDrawLine.pro

$ make

$ ./testQPainterDrawLine
Qt Version: 5.9.4

snapshot of testQPainterDrawLine

因此,最后,我相信,这与QPainter::drawLine()QPainter::drawPolyline()无关。仅仅是缩放比例过高而导致浮点问题。这就是线条可能意外地(消失)出现的原因。

解决方案很简单:在使用QPainter绘制值之前必须先对值进行缩放,以使QPainter中的内部转换发生的幅度更接近于0和1。