从Qt官方给的例程可以看出,如果想将绘图加入事件循环共有两种方式:绘图事件和定时器事件两种方式,通常使用前者。
(一) painterEvent
在绘图事件中,如果想使用update()刷新页面是行不通的,除非使用新的定时器定时刷新页面。painterEvent来自QWidget的virtual protected function,其他一些控件例如QLabel、QLineEdit、QMenuBar等的painterEvent也是继承QWidget的。所以这些有继承QWidget的类中基本都存在painterEvent。
QWidget的继承类 |
Phonon::EffectWidget,Phonon::SeekSlider, Phonon::VideoPlayer,Phonon::VideoWidget, Phonon::VolumeSlider,Q3ComboBox, Q3DataBrowser, Q3DataView,Q3DateTimeEdit, Q3DateTimeEditBase, Q3DockArea,Q3Header, Q3MainWindow, QAbstractButton,QAbstractSlider, QAbstractSpinBox, QAxWidget,QCalendarWidget, QComboBox,QDesignerActionEditorInterface, QDesignerFormWindowInterface,QDesignerObjectInspectorInterface,QDesignerPropertyEditorInterface, QDesignerWidgetBoxInterface, QDesktopWidget,QDialog, QDialogButtonBox, QDockWidget,QFocusFrame, QFrame, QGLWidget, QGroupBox,QHelpSearchQueryWidget, QHelpSearchResultWidget,QLineEdit, QMacCocoaViewContainer,QMacNativeWidget, QMainWindow, QMdiSubWindow,QMenu, QMenuBar, QPrintPreviewWidget,QProgressBar, QRubberBand, QSizeGrip,QSplashScreen, QSplitterHandle, QStatusBar, QSvgWidget, QTabBar, QTabWidget, QToolBar,QWebInspector, QWebView, QWizardPage,QWorkspace, QWSEmbedWidget,QX11EmbedContainer,QX11EmbedWidget. |
这就意味着这些类的子类都能通过重写painterEvent()实现绘图事件循环。
void QWidget::paintEvent ( QPaintEvent * event ) [virtual protected]
以下为Qt官方例程(Basic Drawing)的代码:
/**
* Renderarea.h
*/
#ifndef RENDERAREA_H
#define RENDERAREA_H
#include
#include
#include
#include
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
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坐标系统
QT 坐标系统理解_qt中根据偏移值获取偏移点坐标-CSDN博客
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
#include
#include
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 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();
}
然后其他地方启动绘图的定时器即可。