Создайте проект на основе главного окна и задайте белый цвет фона в свойствах этого окна.
По аналогии с предыдущим примером мы будем запоминать нарисованные окружности в списке. Но для того, чтобы использовать такие списки нам нужно определить класс, описывающий окружности.
Щелкните в окне, описывающем структуру проекта (слева), по имени проекта правой кнопкой мыши и в появившемся контекстном меню выберите Add New...
В появившемся диалоговом окне выберите С++ | C++ Class. Задайте имя класса Circle и завершите создание класса. К проекту будут добавлены файлы circle.h и circle.cpp, содержащие описание нашего класса.
Для описания окружности мы будем использовать координаты центра, радиус и цвет окружности. Хранить в списках можно только такие объекты, для которых определены конструктор по умолчанию, конструктор копирования и оператор присваивания. Кроме того нам потребуется основной коструктор.
Отредактируйте файл circle.h как показано ниже:
#ifndef CIRCLE_H
#define CIRCLE_H
#include <QtGui> // обеспечивает графическую систему
class Circle
{
public:
QPoint p; // координаты центра окружности
int r; // радиус
QColor color; // цвет
Circle(); // конструктор по умолчанию
Circle(QPoint& _p, int _r, QColor col); // основной конструктор
Circle(const Circle&); // конструктор копирования
Circle& operator = (const Circle&); // оператор присваивания
};
#endif // CIRCLE_H
Отредактируйте файл circle.cpp как показано ниже:
#include "circle.h"
Circle::Circle()
{
p = QPoint(0,0);
r = 0;
color = Qt::darkCyan;
}
Circle::Circle(QPoint &_p, int _r, QColor col = Qt::darkCyan)
{
p = _p;
r = _r;
color = col;
}
Circle::Circle(const Circle & circle)
{
p = circle.p;
r = circle.r;
color = circle.color;
}
Circle& Circle::operator =(const Circle & circle)
{
p = circle.p;
r = circle.r;
color = circle.color;
return *this;
}
Перейдите к редактированию дизайна окна. Добавьте в меню команду Файл.
В выпадающее меню добавьте действия: Открыть (actionOpen), Сохранить (actionSave) и Выход (actionQuit). Определите акселераторы для этих действий. Свяжите нажатие кнопки «Выход» со слотом close() главного окна.
Добавьте в меню команду Редактирование. В выпадающее меню добавьте действия: Выбрать цвет (actionColor), Отменить (actionAnnul), Очистить (actionClear).
Отредактируйте файл mainwindow.h как показано ниже:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "circle.h" // обеспечивает использование типа Circle
namespace Ui {
class MainWindow;
}
class MainWindow: public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
QPoint p[2]; // точки, которые определяют текущую окружность
int r; // радиус
bool drawMode; // признак рисования
QList <Circle> DrawBuffer;
// список окружностей (ради этой строчки мы создали класс Circle)
QColor color; // цвет текущей окружности
void mousePressEvent(QMouseEvent *); // обрабатывает нажатие кнопки мыши
void mouseMoveEvent(QMouseEvent *); // обрабатывает движение мыши
void mouseReleaseEvent(QMouseEvent *);
// обрабатывает отпускание кнопки мыши
void paintEvent(QPaintEvent *); // описывает, что нужно нарисовать в окне
public slots:
void Annul(); // для кнопки «Отменить»
void Clear(); // для кнопки «Очистить»
void showDialog(); // для кнопки «Выбрать цвет»
void save(); // для кнопки «Сохранить»
void open(); // для кнопки «Открыть»
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
Отредактируйте файл mainwindow.cpp как показано ниже:
#include "mainwindow.h"
#include "ui_mainwindow.h"
/* Для того, чтобы можно было сохранить список окружностей, нужно научить программу сохранять каждый объект класса Circle: */
QDataStream& operator <<(QDataStream& ostream, const Circle& c)
{
ostream << c.p.x()<< c.p.y() << c.r << c.color;
return ostream;
}
// Описываем, как считывать из потока каждый объект класса circle:
QDataStream& operator >> (QDataStream& istream, Circle& c)
{
int x, y;
istream >> x >> y >> c.r >> c.color;
c.p.setX(x);
c.p.setY(y);
return istream;
}
MainWindow::MainWindow(QWidget *parent):
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// связываем сигналы от команд меню с соответствующими слотами:
connect(ui->actionAnnul, SIGNAL(triggered()), this, SLOT(Annul()));
connect(ui->actionClear, SIGNAL(triggered()), this, SLOT(Clear()));
connect(ui->actionColor, SIGNAL(triggered()), this, SLOT(showDialog()));
connect(ui->actionSave, SIGNAL(triggered()), this, SLOT(save()));
connect(ui->actionOpen, SIGNAL(triggered()), this, SLOT(open()));
// инициализируем элементы данных класса MainWindow:
drawMode = false;
p[0] = p[1] = QPoint(0,0);
r = 0;
color = Qt::darkCyan;
}
MainWindow::~MainWindow()
{
delete ui;
}
// Обрабатываем нажатие кнопки мыши:
void MainWindow::mousePressEvent(QMouseEvent * e)
{
if (e->button() ==Qt::LeftButton) // если нажата левая кнопка
{
drawMode = true; // разрешаем рисование
p[0] = e->pos(); // запоминаем центр окружности
}
}
// Обрабатываем движение мыши:
void MainWindow::mouseMoveEvent(QMouseEvent * e)
{
if (drawMode) // если прижата левая кнопка
{
p[1] = e->pos(); // запоминаем текущую точку на окружности
r = sqrt(pow(p[1].x()-p[0].x(),2) + pow(p[1].y()-p[0].y(),2));
// вычисляем радиус окружности
update(); // перерисовываем окно
}
}
// Обрабатываем отпускание кнопки мыши:
void MainWindow::mouseReleaseEvent(QMouseEvent *)
{
drawMode = false; // запрещаем рисование
int r = sqrt(pow(p[1].x()-p[0].x(),2) + pow(p[1].y()-p[0].y(),2));
// вычисляем текущий радиус
DrawBuffer.append(Circle(p[0], r, color)); // добавляем окружность в список
update(); // перерисовываем окно
}
// Описываем, что нужно нарисовать в окне
void MainWindow::paintEvent(QPaintEvent *)
{
QPainter painter(this);
int size = DrawBuffer.size();
if (size) // Если список не пуст:
{
// рисуем окружности, сохраненные в списке:
QList<Circle>::const_iterator it = DrawBuffer.begin();
// итератор для прохода по списку
do
{
Circle c = *it++;
painter.setPen(c.color);
painter.drawEllipse(c.p, c.r, c.r);
} while (it!= DrawBuffer.end());
// рисуем окружности, пока список не закончится
}
/* рисуем текущую окружность: окружность изменяется в зависимости от движения мыши, поэтому мы ее будем рисовать отдельно в объекте pixmap и накладывать этот объект на окно, тогда не нужно стирать предыдущий вид этой окружности */
if (drawMode) // если прижата левая кнопка мыши
{
// определяем размеры области рисования:
int w = width();
int h = height()-(ui->statusBar->height());
QPixmap pixmap(w, h);
// создаем область, в которой мы будем рисовать текущую окружность
pixmap.fill(Qt::transparent); // делаем фон прозрачным
QPainter pntPixmap(&pixmap); // создаем объект для рисования
pntPixmap.setRenderHint(QPainter::Antialiasing); // сглаживание
pntPixmap.setPen(color);
/* устанавливаем цвет пера (который мы можем выбрать с помощью специального окна) */
pntPixmap.drawEllipse(p[0],r,r);
// рисуем текущую окружность в области pixmap
painter.drawPixmap(0, 0, w, h, pixmap);
// накладываем текущую окружность на ранее нарисованные
}
}
// Слот для кнопки «Выбор цвета»:
void MainWindow::showDialog()
{
color = QColorDialog::getColor();
// выводим на экран специальное окно и считываем цвет
}
// Слот для кнопки «Отменить»:
void MainWindow::Annul()
{
DrawBuffer.removeLast(); // удаляем последний элемент из списка
update();
}
// Слот для кнопки «Очистить»:
void MainWindow::Clear()
{
DrawBuffer.clear(); // Очищаем список
update();
}
// Слот для кнопки «Открыть»
void MainWindow::open()
{
QFile file("picture.dat");
file.open(QIODevice::ReadOnly); // Создаем файл для чтения
QDataStream in(&file); // Создаем поток на основе файла
in>>DrawBuffer; // считываем список окружностей из потока
update(); // отображаем окружности в окне
}
// Слот для кнопки «Сохранить»
void MainWindow::save()
{
QFile file("picture.dat"); // создаем файл для записи
file.open(QIODevice::WriteOnly);
QDataStream out(&file); // создаем поток на основе файла:
out << DrawBuffer; // записываем окружности в файл
file.close(); // обязательно закрываемфайл
}
Запустите программу, нарисуйте несколько окружностей разного цвета и проверьте работу команд меню.