В связи с большим количеством однотипных вопросов от начинающих пользоваться QPainter, и людей, задающихся вопросом "Как нарисовать что-то своё на виджете", я распишу базовые понятия рисования.
Итак, начнём с главного правила - рисовать на виджете можно переопределив его метод paintEvent(QPaintEvent *).
Причина переопределения метода перерисовки в том, что код, выполняющийся в этой функции, будет запущен каждый раз, когда надо перерисовать внешний вид виджета. Перерисовка, на самом деле, требуется на так уж часто - когда изменяется геометрия виджета (например мы растянули окно, и вместе с ним растянулся наш виджет), когда виджет перекрывается/перестаёт_перекрываться/изменилось_перекрывание другими виджетами - вот основные причины для автоматического вызова перерисовки.
Кстати говоря, автоматического вызова перерисовки не всегда достаточно, но мы можем сами вызвать перерисовку, вызвав метод update() для виджета.
Подводя итог, можно сказать следующее: если мы хотим нарисовать что-то на виджете - нам надо создать свой собственный виджет, и переопределить его метод paintEvent(QPaintEvent *). После этого, мы создадим объект нашего класса.
1. Создаём наш виджет, который позволяет рисовать на себе:
qpaintwidget.h
#ifndef QPAINTWIDGET_H
#define QPAINTWIDGET_H
#include <QWidget>
class QPaintEvent;
class QPaintWidget : public QWidget
{
Q_OBJECT
public:
QPaintWidget(QWidget * parent = 0);
protected:
void paintEvent(QPaintEvent *);
};
#endif // QPAINTWIDGET_H
qpaintwidget.cpp
#include "qpaintwidget.h"
#include <QPainter>
QPaintWidget::QPaintWidget(QWidget * parent) : QWidget(parent)
{
}
void QPaintWidget::paintEvent(QPaintEvent *) {
QPainter p(this); // Создаём новый объект рисовальщика
p.setPen(QPen(Qt::red,1,Qt::SolidLine)); // Настройки рисования
p.drawLine(0,0,width(),height()); // Рисование линии
}
Пояснения:
QPainter p(this); - создаём новый объект рисовальщика. Рисовать мы можем, по идее, на любом QPaintDevice, указав указатель на объект унаследованный от QPaintDevice в качестве параметра для создающегося QPainter объекта. (Подробнее о классах, на которых можно рисовать - QtAssitant::QPaintDevice;) В нашем случае мы передаём указатель на наш виджет - this. После этого наш объект QPainter класса может совершенно спокойно рисовать.
p.setPen(QPen(Qt::red,1,Qt::SolidLine)); - настраиваем рисование. а точнее говорим, что рисовать надо красным цветом, сплошными линиями толщиной 1 пиксель.
p.drawLine(0,0,width(),height()); - рисуем линию из точки с координатами (0,0) (по умолчанию это верхний левый угол виджета, однако это можно изменить) в точку с координатами (width,height).
В результате, мы добъёмся того, что у нас на виджете всегда будет рисоваться диагональная красная линия.
2. Небольшое пояснение о системах координат:
По умолчанию виджет имеет систему координат с началом в верхнем левом углу (точка (0,0) ). Ось X направлена вправо, ось Y направлена вниз. Однако это всегда можно изменить функцией setWindow() - подробности в QtAssistant.
3. Осталось создать объект нашего класса.
Я создал стандартный проект с MainWindow окном. Потом положил на него вертикальный лейаут (но, разумеется, можно любой, и вообще - это просто для удобства всего лишь один способ разместить виджет на другом виджете) что бы в него помещать наш виджет.
Модифицированные строки выделены комментариями.
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
//nash class
#include "qpaintwidget.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
void changeEvent(QEvent *e);
private:
Ui::MainWindow *ui;
// Указатель на объект нашего класса
QPaintWidget *wgt;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// Создаем виджет
wgt = new QPaintWidget(this);
// И кладём его в лайаут
ui->verticalLayout->addWidget(wgt);
}
MainWindow::~MainWindow()
{
// Подчищаем
wgt->deleteLater();
delete ui;
}
void MainWindow::changeEvent(QEvent *e)
{
QMainWindow::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}
4. Вот и всё.
Что же происходит? Cоздаётся виджет QPaintWidget *wgt, его размером управляет лейаут. Как только произошло изменение размеров, у виджета вызывается метод paintEvent(QPaintEvent *), в котором мы на этом виджете рисуем диагональную линию красным цветом.
5. Послесловие
Разумеется, этого редко бывает достаточно. Если мы действительно хотим что-то рисовать на виджете (например, графики), то в ход идут немного другие алгоритмы.
Например:
5.1. В нашем виджете создаём QImage объект и пишем функции синхронизации размеров этого объкта с размерами виджета. Это будет наш, так называемый, Back Buffer - фоновый буффер для рисования.
5.2. Когда нам надо что-то нарисовать - мы рисуем именно на этой "картинке". Рисование может занимать долгое время, может короткое - мы можем вынести этот код в отдельный поток - это не проблема. Просто результатом потока будет объект QImage на котором будет нарисовано всё что надо (а пока будет расчитываться и рисоваться - мы будем сигналить процент прогресса).
5.3. При необходимости рисования мы один раз рисуем фоновый буфер на наш виджет (QImage объект на котором УЖЕ нарисовано всё что нам надо) методом QPainter::drawImage(...) внутри функции paintEvent(QPaintEvent *). Тем самым мы добъёмся того, что бы отрисовка происходила плавно и не загружала основной поток.
Надеюсь, этот маленький HOWTO резко сократит количество вопросов по рисованию с QPainter.