目录
一、Qt绘图基础:事件、画笔、字体、画刷与画家
在Qt中创建图形界面或进行自定义绘制时,理解其核心绘图机制至关重要。这一切通常围绕着绘图事件以及几个关键的类展开。
1、绘图事件(Paint Event)
Qt的绘图不是即时发生的,而是通过事件驱动的。当窗口首次显示、被其他窗口遮挡后重新显示、窗口大小改变,或者我们显式地调用QWidget::update()
或QWidget::repaint()
时,Qt会向窗口发送一个绘图事件(Paint Event)。
为了响应这个事件并执行自定义的绘制逻辑,我们需要重写(Override) QWidget(或其子类,如QPushButton、QLabel等)中的paintEvent(QPaintEvent *event)
虚函数。
protected:
void paintEvent(QPaintEvent *event) override {
// 在这里执行所有的绘图操作
Q_UNUSED(event); // 通常我们只关心需要绘制的区域,event->region()可以获取,但简单绘制时可忽略
}
当Qt调用我们的paintEvent
函数时,我们就获得了在特定窗口或控件上绘制的机会。在这个函数内部,我们通常会创建一个QPainter
对象,并利用它来进行实际的绘图工作。
2、画家(QPainter)
QPainter
是Qt绘图系统的核心类,可以将其理解为一位“画家”,负责实际执行绘图操作。我们通常在paintEvent
函数内部创建一个QPainter
对象,并将其与需要绘制的窗口或控件(通过begin()
或更推荐的QPainter
构造函数/重载的drawXXX
函数的device
参数关联起来)。
void MyWidget::paintEvent(QPaintEvent *event) {
QPainter painter(this); // 创建画家,并指定绘制目标为当前窗口(this)
// ... 设置画笔、画刷、字体等
// ... 调用drawRect, drawText等绘图函数
}
QPainter
提供了丰富的绘图函数,如drawRect()
, drawLine()
, drawText()
, drawPixmap()
等,用于绘制各种图形和图像。
3、画笔(QPen)
QPen
定义了绘制线条和形状轮廓的样式。它决定了线条的颜色、宽度、样式(如实线、虚线、点线)等。
QPen pen(Qt::black, 2, Qt::SolidLine); // 创建画笔:黑色实线,宽度为2
painter.setPen(pen); // 将设置好的画笔应用到画家上
painter.drawRect(50, 50, 200, 100); // 使用当前画笔绘制矩形框
这段代码表示:在paintEvent
中,我们创建了一个黑色、宽度为2像素的实线画笔,并将其设置为当前QPainter
使用的画笔。然后,调用drawRect
绘制一个矩形。此时,这个矩形的边框将呈现为黑色、宽度为2像素的实线。画笔决定了框的边框样式和颜色。
4、字体(QFont)
QFont
定义了绘制文本的字体属性,如字体家族、大小、重量(粗体/正常)、斜体等。
QFont font("SimSun", 12, QFont::Bold); // 创建字体:宋体,12号字,粗体
painter.setFont(font); // 将设置好的字体应用到画家上
painter.drawText(70, 80, "Hello, World!"); // 使用当前字体在指定位置绘制文本
这段代码表示:在paintEvent
中,我们创建了一个宋体、12号、粗体的字体,并将其设置为当前QPainter
使用的字体。然后,调用drawText
在坐标(70, 80)处绘制文本"Hello, World!"。此时,文本将使用指定的宋体、12号粗体样式显示。字体决定了文本的样式和大小。
5、画刷(QBrush)
QBrush
定义了填充形状内部的样式。它可以是纯色、渐变色或图案。
QBrush brush(Qt::yellow); // 创建画刷:黄色填充
painter.setBrush(brush); // 将设置好的画刷应用到画家上
painter.drawRect(50, 50, 200, 100); // 使用当前画刷填充矩形
二、综合示例一
结合上述所有组件,我们可以完整地实现Pointer画框、写字、涂色的场景:
#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QPen>
#include <QFont>
#include <QBrush>
class MyWidget : public QWidget {
protected:
void paintEvent(QPaintEvent *event) override {
QPainter painter(this);
// 1. 画笔:绘制矩形框
QPen pen(Qt::black, 2, Qt::SolidLine);
painter.setPen(pen);
painter.drawRect(50, 50, 200, 100);
// 2. 字体:在框内写宋体字
QFont font("SimSun", 12, QFont::Bold);
painter.setFont(font);
painter.drawText(70, 80, "Hello, World!");
// 3. 画刷:用黄色填充框
QBrush brush(Qt::yellow);
painter.setBrush(brush);
painter.drawRect(50, 50, 200, 100);
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
MyWidget widget;
widget.resize(300, 200);
widget.show();
return app.exec();
}
这段代码展示了如何结合QPainter、QPen、QFont和QBrush来实现Pointer的场景。首先用画笔绘制矩形框,然后用字体在框内写宋体字,最后用画刷将框填充为黄色。
三、综合示例二
在box框内显示图片,按左右键更新图片;
picturesdialog.cpp
#include "picturesdialog.h"
#include "ui_picturesdialog.h"
#include <QPainter>
PicturesDialog::PicturesDialog(QWidget *parent)
: QDialog(parent)
, ui(new Ui::PicturesDialog)
,m_imageIndex(0)
{
ui->setupUi(this);
enableButtons();
}
PicturesDialog::~PicturesDialog()
{
delete ui;
}
void PicturesDialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QRect framRect = ui->m_frmImage->rect();
framRect.translate(ui->m_frmImage->pos());
QImage image(":/images/" + QString::number(m_imageIndex) + ".jpg");
painter.drawImage(framRect,image);
}
void PicturesDialog::on_m_btnPrev_clicked()
{
--m_imageIndex;
enableButtons();
update();
}
void PicturesDialog::on_m_btnNext_clicked()
{
++m_imageIndex;
enableButtons();
update();
}
void PicturesDialog::enableButtons()
{
ui->m_btnPrev->setEnabled(m_imageIndex>0);
ui->m_btnNext->setEnabled(m_imageIndex<6);
}
picturesdialog.h
#ifndef PICTURESDIALOG_H
#define PICTURESDIALOG_H
#include <QDialog>
QT_BEGIN_NAMESPACE
namespace Ui { class PicturesDialog; }
QT_END_NAMESPACE
class PicturesDialog : public QDialog
{
Q_OBJECT
public:
PicturesDialog(QWidget *parent = nullptr);
~PicturesDialog();
protected:
void paintEvent(QPaintEvent *);
private slots:
void on_m_btnPrev_clicked();
void on_m_btnNext_clicked();
private:
void enableButtons();
private:
Ui::PicturesDialog *ui;
int m_imageIndex;
};
#endif // PICTURESDIALOG_H
picturesdialog.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PicturesDialog</class>
<widget class="QDialog" name="PicturesDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>315</width>
<height>560</height>
</rect>
</property>
<property name="windowTitle">
<string>图片</string>
</property>
<layout class="QVBoxLayout" name="m_layoutVer">
<item>
<widget class="QFrame" name="m_frmImage">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="m_layoutHor">
<item>
<spacer name="m_spacerLeft">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="m_btnPrev">
<property name="text">
<string>上一张</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_btnNext">
<property name="text">
<string>下一张</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="m_spacerRight">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
效果图: