Qt之PaintEvent

从Qt官方给的例程可以看出,如果想将绘图加入事件循环共有两种方式:绘图事件和定时器事件两种方式,通常使用前者。

(一) painterEvent

在绘图事件中,如果想使用update()刷新页面是行不通的,除非使用新的定时器定时刷新页面。painterEvent来自QWidget的virtual protected function,其他一些控件例如QLabel、QLineEdit、QMenuBar等的painterEvent也是继承QWidget的。所以这些有继承QWidget的类中基本都存在painterEvent。

QWidget的继承类

Phonon::EffectWidget,Phonon::SeekSliderPhonon::VideoPlayer,Phonon::VideoWidget,

 Phonon::VolumeSlider,Q3ComboBoxQ3DataBrowserQ3DataView,Q3DateTimeEdit

Q3DateTimeEditBaseQ3DockArea,Q3HeaderQ3MainWindowQAbstractButton,QAbstractSlider,

 QAbstractSpinBoxQAxWidget,QCalendarWidgetQComboBox,QDesignerActionEditorInterface,

QDesignerFormWindowInterface,QDesignerObjectInspectorInterface,QDesignerPropertyEditorInterface,

QDesignerWidgetBoxInterfaceQDesktopWidget,QDialogQDialogButtonBoxQDockWidget,QFocusFrame

QFrameQGLWidgetQGroupBox,QHelpSearchQueryWidgetQHelpSearchResultWidget,QLineEdit

QMacCocoaViewContainer,QMacNativeWidgetQMainWindowQMdiSubWindow,QMenuQMenuBar

QPrintPreviewWidget,QProgressBarQRubberBandQSizeGrip,QSplashScreenQSplitterHandleQStatusBar,

QSvgWidgetQTabBarQTabWidgetQToolBar,QWebInspectorQWebViewQWizardPage,QWorkspace

QWSEmbedWidget,QX11EmbedContainer,QX11EmbedWidget.

 这就意味着这些类的子类都能通过重写painterEvent()实现绘图事件循环。

void QWidget::paintEvent ( QPaintEvent * event ) [virtual protected]

以下为Qt官方例程(Basic Drawing)的代码:

/**
*  Renderarea.h
*/
#ifndef RENDERAREA_H
 #define RENDERAREA_H
 #include <QBrush>
 #include <QPen>
 #include <QPixmap>
 #include <QWidget>
 class RenderArea : public QWidget
 {
     Q_OBJECT
 public:
     enum Shape { Line, Points, Polyline, Polygon, Rect, RoundedRect, Ellipse, Arc,
                  Chord, Pie, Path, Text, Pixmap };
     RenderArea(QWidget *parent = 0);
     QSize minimumSizeHint() const;
     QSize sizeHint() const;
 public slots:
     void setShape(Shape shape);
     void setPen(const QPen &pen);
     void setBrush(const QBrush &brush);
     void setAntialiased(bool antialiased);
     void setTransformed(bool transformed);
 protected:
     void paintEvent(QPaintEvent *event);
 private:
     Shape shape;
     QPen pen;
     QBrush brush;
     bool antialiased;
     bool transformed;
     QPixmap pixmap;
 };
 #endif
/**
*    Window.h
*/ 
#ifndef WINDOW_H
 #define WINDOW_H
 #include <QWidget>
 class QCheckBox;
 class QComboBox;
 class QLabel;
 class QSpinBox;
 class RenderArea;
 class Window : public QWidget
 {
     Q_OBJECT
 public:
     Window();
 private slots:
     void shapeChanged();
     void penChanged();
     void brushChanged();
 private:
     RenderArea *renderArea;
     QLabel *shapeLabel;
     QLabel *penWidthLabel;
......	
 };
 #endif

然后在window.cpp文件中将renderArea对象按照qwidget处理即可;

这样处理完后即可进入事件循环了。

下来就是重头戏:在painterEvent()里画图!

在画图之前需要前置知识点:

(1)  Qt坐标系统

https://blog.csdn.net/hgcprg/article/details/53537106

https://www.cnblogs.com/lifexy/p/9203929.html

但是上面两篇关于视口和窗口的介绍没怎么看明白,得多琢磨下。

/**
* Painter.cpp
*/
QPainter Painter(this);
	Painter.setRenderHint(QPainter::Antialiasing, true);///抗锯齿
	QPainterPath Path,Path1;    ///路径
	QSize ViewSize(100,100);
	const QRectF Xrectf[5] ={
		QRectF(1,19,1.5,18),
		QRectF(5,19.1,6,18),
		QRectF(9.5,19.1,10,18),
		QRectF(13.5,19.1,14,18),
		QRectF(17.5,19.1,18,18)
	};
	const QRectF Yrectf[5] ={          ///字的位置
		QRectF(0.5,19,1.5,19.5),
		QRectF(0.5,15,1.5,15.5),
		QRectF(0.5,11,1.5,11.5),
		QRectF(0.5,7,1.5,7.5),
		QRectF(0.5,3,1.5,3.5)
	};
	QPolygonF polygon,polygon1;
	const QPoint Xpoints[5]= {
		QPoint(2,20),
		QPoint(6,20),
		QPoint(10,20),
		QPoint(14,20),
		QPoint(18,20)
	};
	const QPoint Ypoints[5]= {
		QPoint(2,20),
		QPoint(2,16),
		QPoint(2,12),
		QPoint(2,8),
		QPoint(2,4)
	};

	QFont font("Arial",1,QFont::Black);
	Painter.scale(15,15);                     ///x,y放大15倍
	//Painter.rotate(-45);                     ///旋转角度
	Painter.setViewport(0,0,ViewSize.width(),ViewSize.height());
	Painter.setWindow(0,0,ViewSize.width(),/*-1**/ViewSize.height());
	QPen Pen(Qt::red, 0, Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin); ///红色,实线
	Painter.setPen(Pen);
	Painter.setBrush(Qt::NoBrush);         ///不用刷子
	Painter.drawLine(1,20,22,20);			///连(1,20)->(22,20)
	Painter.drawLine(2,2,2,22);
	Path.moveTo(1,3), Path.lineTo(2,2), Path.lineTo(3,3);
	Path1.moveTo(21,19),Path1.lineTo(22,20),Path1.lineTo(21,21);///路径
	Painter.drawPath(Path),Painter.drawPath(Path1);
	QPen Pen1(Qt::blue, 0.2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
	Painter.setFont(font);               ///设置字体 arial 字宽1, 黑色
	Painter.setPen(Pen1);
	for(int i = 0; i < 5;i++)
	{
		Painter.drawPoint(Xpoints[i]);
		Painter.drawPoint(Ypoints[i]);
		Painter.drawText(Xrectf[i],QString::number(i*5));
		Painter.drawText(Yrectf[i],QString::number(i*5));
	}
	QPen Pen2(Qt::yellow);
	QPolygon polygon2;
	int x = generateRandomNumber(19);
	int y = generateRandomNumber(20);
	polygon2 << TransformToNewAxis(0,0) << TransformToNewAxis(0,y) << TransformToNewAxis(x,y) << TransformToNewAxis(x+1,0);   ///多边形
	Painter.setPen(Pen2);
	const QColor colors[5] = {
		QColor(16,78,139),
		QColor(255,62,150),
		QColor(58,95,205),
		QColor(0,238,0),
		QColor(153,50,204)
	};
	int col = generateRandomNumber(5);
	QBrush shape(colors[col],Qt::SolidPattern);
	Painter.setBrush(shape);         ///给多边形填充颜色
	Painter.drawPolygon(polygon2);
	this->update();

(二)TimerEvent

在timerEvent就比较容易控制绘图的刷新了,因为是定时器事件,可以控制时间。在官方例程中也提供了这种方法:

/**
*    PathDeform.h
*/
#ifndef PATHDEFORM_H
 #define PATHDEFORM_H

 #include "arthurwidgets.h"
 #include <QPainterPath>
 #include <QBasicTimer>
 #include <QDateTime>
 class PathDeformRenderer : public ArthurFrame
 {
     Q_OBJECT
     Q_PROPERTY(bool animated READ animated WRITE setAnimated)
     Q_PROPERTY(int radius READ radius WRITE setRadius)
     Q_PROPERTY(int fontSize READ fontSize WRITE setFontSize)
     Q_PROPERTY(int intensity READ intensity WRITE setIntensity)
     Q_PROPERTY(QString text READ text WRITE setText)
 public:
     PathDeformRenderer(QWidget *widget, bool smallScreen = false);
     void paint(QPainter *painter);
     void mousePressEvent(QMouseEvent *e);
     void mouseReleaseEvent(QMouseEvent *e);
     void mouseMoveEvent(QMouseEvent *e);
     void timerEvent(QTimerEvent *e);
     QSize sizeHint() const { return QSize(600, 500); }
     bool animated() const { return m_animated; }
     int radius() const { return int(m_radius); }
     int fontSize() const { return m_fontSize; }
     int intensity() const { return int(m_intensity); }
     QString text() const { return m_text; }

 public slots:
     void setRadius(int radius);
     void setFontSize(int fontSize) { m_fontSize = fontSize; setText(m_text); }
     void setText(const QString &text);
     void setIntensity(int intensity);
     void setAnimated(bool animated);
 signals:
     void clicked();
 private:
     void generateLensPixmap();
     QPainterPath lensDeform(const QPainterPath &source, const QPointF &offset);

     QBasicTimer m_repaintTimer;
     QTime m_repaintTracker;
     QVector<QPainterPath> m_paths;
 .......
 };///

/**
*    PathDeform.cpp
*/
 void PathDeformRenderer::timerEvent(QTimerEvent *e)
 {
     if (e->timerId() == m_repaintTimer.timerId()) { //判断定时器id是否绘图定时器的ID
         if (QLineF(QPointF(0,0), m_direction).length() > 1)
             m_direction *= 0.995;
         qreal time = m_repaintTracker.restart();
         QRect rectBefore = circle_bounds(m_pos, m_radius, m_fontSize);
         qreal dx = m_direction.x();
         qreal dy = m_direction.y();
         if (time > 0) {
             dx = dx * time * .1;
             dy = dy * time * .1;
         }
         m_pos += QPointF(dx, dy);
         if (m_pos.x() - m_radius < 0) {
             m_direction.setX(-m_direction.x());
             m_pos.setX(m_radius);
         } else if (m_pos.x() + m_radius > width()) {
             m_direction.setX(-m_direction.x());
             m_pos.setX(width() - m_radius);
         }
         if (m_pos.y() - m_radius < 0) {
             m_direction.setY(-m_direction.y());
             m_pos.setY(m_radius);
         } else if (m_pos.y() + m_radius > height()) {
             m_direction.setY(-m_direction.y());
             m_pos.setY(height() - m_radius);
         }
 #ifdef QT_OPENGL_SUPPORT
         if (usesOpenGL()) {
             update();
         } else
 #endif
         {
             QRect rectAfter = circle_bounds(m_pos, m_radius, m_fontSize);
             update(rectAfter | rectBefore);
             QApplication::syncX();
         }
     }
 }
 void PathDeformRenderer::mouseReleaseEvent(QMouseEvent *e)
 {
     if (e->buttons() == Qt::NoButton && m_animated) {
         m_repaintTimer.start(10, this);
         m_repaintTracker.start();
     }
     if (!m_mouseDrag && m_smallScreen)
         emit clicked();
 }

然后其他地方启动绘图的定时器即可。

©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页