4

I am a beginner in Qt, and I want to use QPainter.

My process is like this: I receive data coordinates (x,y) from the serial port, like (1,1), (2,3), etc. I want to draw these points in a window every time I receive data.

I see the QPainter is used in events, and just paints one time. How can I use it every time I receive data? Just like a have a signal DataCome() and a slot Paint().\


By the Way ,thx a lot to the Answer.Your advise is very Useful .

In short ,updata() or repaint() is work in this case .

I have another question . Assume ,the serial port continuous to send the coordinate points to computer, and I want to display all the point in the window. Is there some method ,I can leave those points came early on the window,and I just need to paint the new points?Like "hold on " in matlab. Or I need a container to store the coordinates ,and paint all of them very time.

4

4 回答 4

4

我已经设置了一个快速示例,希望能帮助您了解完成任务所需使用的机制。

它由一个Listener侦听数据并将其发送到Widget绘图的类组成。在我的示例中,我对其进行了设置,以便使用计时器随机生成并定期发送数据,但在您的情况下,这将是您的串行端口数据。

由于我假设您要做的是一个绘图,因此您不能使用paintEvent来绘制单点,因为每次它只会显示一个点并且点数据不会累积,所以您需要绘制一个像素图,您只需显示在paintEvent.

以下是 Widget 和 Listener 类:

class Widget : public QWidget {
    Q_OBJECT

public:
    Widget(QWidget *parent = 0) : QWidget(parent) {
        resize(200, 200);
        p = new QPixmap(200, 200);
    }

protected:
    void paintEvent(QPaintEvent *) {
        QPainter painter(this);
        painter.drawPixmap(0, 0, 200, 200, *p);
    }

public slots:
    void receiveData(int x, int y) {
        QPainter painter(p);
        painter.setBrush(Qt::black);
        QPoint point(x, y);
        painter.drawPoint(point);
        data.append(point);
        repaint();
    }

private:
    QPixmap *p;
    QVector<QPoint> data;
};


class Listener : public QObject {
    Q_OBJECT

public:
    Listener(QObject *p = 0) : QObject(p) {
        QTimer * t = new QTimer(this);
        t->setInterval(200);
        connect(t, SIGNAL(timeout()), this, SLOT(sendData()));
        t->start();
    }

signals:
    void dataAvaiable(int, int);

public slots:
    void sendData() {
        emit dataAvaiable(qrand() % 200, qrand() % 200);
    }
};

...主要:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    Listener l;

    QObject::connect(&l, SIGNAL(dataAvaiable(int,int)), &w, SLOT(receiveData(int,int)));

    w.show();

    return a.exec();
}

所以发生的情况是每 200 毫秒生成一个随机数据,发送到小部件,在那里它被添加到像素图并Widget更新以显示新条目。

编辑:考虑到一个点(像素)有多小,你可能想画小圆圈。您还可以根据其数据值对点进行着色,这样您就可以获得渐变,例如低值可能是绿色,但它得到的越高,它会变成黄色,最后变成红色......

如果您以后需要它,您还可能希望将接收到的数据添加到 a QVector<QPoint>,这可以在receiveData插槽中完成。

可能值得一提的另一件事 - 在示例中,所有内容都在 0-200 范围内,数据,绘图窗口 - 非常方便。实际上情况并非如此,因此您需要将数据映射到绘图大小,这可能会根据小部件大小而变化。

这是我常用来规范化某个范围内的值的模板。您可能希望根据您的要求对其进行一些简化。

template <typename Source, typename Target>
Target normalize(Source s, Source max, Source min, Target floor, Target ceiling) {
    return ((ceiling - floor) * (s - min) / (max - min) + floor);
}

Edit2:添加了data向量以数字形式存储所有接收到的点。

于 2013-08-28T06:44:37.400 回答
4

QPainter 可以对任何继承自 QPaintDevice 的对象进行操作。

一个这样的对象是 QWidget。当希望 QWidget 重新渲染时,您可以调用 repaint 或使用需要重新渲染的矩形区域进行更新。

repaint立即导致paintEvent 发生,而update在事件队列上发布一个paintEvent。这两个都是插槽,因此将它们连接到来自另一个线程的信号应该是安全的。

然后你必须重写虚拟方法“paintEvent”并使用小部件创建一个画家:

void MyWidget::paintEvent( QPaintEvent * evt )
{
  QPainter painter( this );
  //... do painting using painter.
}

您可以查看随 Qt 帮助分发的 AnalogClock 示例。

于 2013-08-28T03:52:32.000 回答
1

您只在 QWidget 的paintEvent 中使用 QPainter。你可以这样做:

将接收到的点列表保留为成员,并在paintEvent 中遍历此列表并绘制所需的点。当收到一个新点时,将其添加到列表中并调用 widget->update()。这告诉小部件刷新自己,并且在适当的时候小部件将调用paintEvent。

于 2013-08-28T05:53:37.770 回答
0

创建一个QPixmap实例,然后像这样绘制:

QPixmap pixmap(100, 100);
QPainter p(&pixmap);
// do some drawing

然后,您可以随心所欲地使用像素图:在绘制事件中绘制它,将其写入磁盘......

于 2013-08-28T06:13:01.153 回答