Пример 1: программа, которая рисует 6-угольник, цвет которого можно изменять с помощью немодального диалогового окна с наборными счетчиками (Spin). Диалоговое окно появляется при нажатии кнопки «Диалог».
Создайте проект на базе главного окна.
Добавьте дочернее диалоговое окно, описываемое классом Dialog (выберите в меню Файл | Новый файл или проект..., в появившемся диалоговом окне выберите Qt | Класс формы Qt Designer).
Спроектируйте окно как показано на рисунке, добавив элемент spinBox.
В файл main.cpp добавьте подчеркнутую строку:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.move(400, 250); // перемещает окно к центру экрана
w.show();
return a.exec();
}
В файлe dialog.h в описание класса добавьте подчеркнутые строки:
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
namespace Ui {
class Dialog;
}
class MainWindow; // С помощью этого класса объявим указатель на родительское окно
class Dialog: public QDialog
{
Q_OBJECT
public:
// Измените тип указателя на MainWindow
explicit Dialog(MainWindow *parent = 0);
~Dialog();
MainWindow* Parent; /* С помощью этого указателя диалоговое окно будет передавать новое значение цвета главному окну */
|
|
// объявление слота, который будет передавать новое значение цвета главному окну:
public slots:
void setN(int);
private:
Ui::Dialog *ui;
};
#endif // DIALOG_H
В файл dialog.cpp добавьте подчеркнутые строки:
#include "dialog.h"
#include "ui_dialog.h"
#include "mainwindow.h"
// обеспечивает возможность использования класса MainWindow
Dialog::Dialog(MainWindow *parent):
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
ui->spinBox->setRange(0, 255);
// Задаем диапазон изменнения значений наборного счетчика
ui->spinBox->setSingleStep(5);
// при щелчке по стрелочке значение счетчика будет изменяться на 5
Parent = parent;
// Указатель, объявленный в классе, становится равным значению локального указателя
ui->spinBox->setValue(Parent->n);
/* Устанавливаем начальное значение счетчика так, чтобы оно было равно начальному значению цвета, установленного в конструкторе главного окна */
connect(ui->spinBox, SIGNAL(valueChanged(int)),
this, SLOT(setN(int))); /* связываем сигнал об изменении значения счетчика со слотом, который передает новое значение цвета главному окну */
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::setN(int n) /* значение n автоматически передается слоту от наборного счетчика с помощью сигнала valueChanged(int)
{
Parent->n = n;
// передаем значение цвета переменной, объявленной в классе гланого окна
Parent->update(); // посылаем сигнал «перерисовать окно»
}
Добавьте в файл mainwindow.h подчеркнутые строки:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "dialog.h" // Обеспечивает возможность использования класса Dialog
#include <QtGui> // обеспечивает возможность использования графических функций
namespace Ui {
class MainWindow;
}
class MainWindow: public QMainWindow
|
|
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void paintEvent(QPaintEvent *);
// функция, которая автоматически вызывается при перерисовывании окна
Dialog* dlg; // объект, описывающий диалоговое окно
int n; // значение яркости цвета
private:
Ui::MainWindow *ui;
private slots:
void on_actionDialog_triggered();
};
#endif // MAINWINDOW_H
В режиме проектирования формы с помощью редактора действий добавьте действие actionDialog с текстом «Диалог» (это будет командой меню, которая вызывает вспомогательное диалоговое окно).
В файл mainwindow.cpp добавьте код, показанный ниже:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent):
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
n = 100; // начальное значение цвета
dlg = new Dialog(this); // создаем объект класса Dialog
ui->menuBar->addAction(ui->actionDialog);
// В меню создаем кнопку, соответствующую команде «Диалог» (без выпадающего меню)
}
MainWindow::~MainWindow()
{
delete ui;
}
// В режиме проектирования формы добавьте слот для действия actionDialg:
void MainWindow::on_actionDialog_triggered()
{
dlg->move(400+width(), 250);
// размещаем диалоговое окно на правой границе главного окна
dlg->show(); // рисуем окно на экране
dlg->activateWindow(); // делаем диалоговое окно активным
}
// Описываем, что нужно нарисовать в окне
void MainWindow::paintEvent(QPaintEvent *)
{
QPainter painter(this);// Объект для рисования
QString str = QString::number(n);/* Строка, с помощью которой на экране будет отображаться текущая яркость цвета */
painter.drawText(20,70, str);// Отображаем на экране значение яркости цвета
QBrush brush(QColor(n,0,0));/* объявляем кисть, число n в данном случае будет опреелять яркость красного цвета */
painter.setBrush(brush);// побключаем кисть
QPolygonF polygon; // Объявляем многоугольник
qreal r = 100; // радиус окружности, в которую будет вписан многоугольник
qreal x0 = 200, y0 = 150; // центр окружности
qreal dfi = M_PI / 3; // угол между вершинами 6-угольника
for (int i = 0; i< 6; ++i) // рисуем 6-угольник
{
qreal fi = dfi * i; // угол, соответствующий очередной вершине
// определяем координаты вершины
qreal x = x0 + r*cos(fi);
qreal y = y0 + r*sin(fi);
polygon << QPointF(x,y);// добавляем координаты вершины к полигону
}
painter.drawPolygon(polygon);// рисуем многоугольник (6-угольник)
}
Пример 2: программа, которая расчерчивает окно правильными 6-угольниками («пчелиными сотами»); размер шестиугольника — 1 / 5 меньшей стороны окна (но не меньше 2); при щелчке мышью внутри шестиугольника, он закрашивается цветом, выбранным с помощью специального окна для выбора цвета.
Создайте приложение на базе главного окна. В файле mainwindow.h добавьте объявления как показано ниже.
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#define maxR 500 // максимальное количество строк
#define maxC 700 // максимальное количество столбцов
namespace Ui {
class MainWindow;
}
class MainWindow: public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void paintEvent(QPaintEvent *);
// автоматически вызывается при перерисовке окна
void createMatrices();
// функция, размещающая в памяти матрицы полигонов и регионов
void createNet(); // формирование сетки в рабочей области окна
void createPolygon(int, int); // создание одной ячейки с заданными номерами
void mousePressEvent(QMouseEvent *);
// автоматически вызыватся при щелчке мышью
void resizeEvent(QResizeEvent *);
// автоматически вызывается при создании окни и при изменении его размеров
QPolygon** Polygon; // Указатель на матрицу полигонов
QRegion** Region; // Указатель на матрицу областей
QColor Color[maxR][maxC]; /* матрица для хранения цвета в каждой ячейке, она должна быть максимальных размеров, поскольку мы заранее не знаем размеры окна */
int previousRows, rows, columns; /* количество строк для предыдущего состояния окна, текущее количество строк, количество столбцов */
double w, h; // размеры рабочего поля
double size, x0, y0, r; // размер одной ячейки, центр и радиус окружности, в которую вписан многоугольник
QBrush brush; // кисть
private:
Ui::MainWindow *ui;
private slots:
void on_actionOpen_triggered();
void on_actionSave_triggered();
};
#endif // MAINWINDOW_H
В файле mainwindow.cpp напишите код, показанный ниже:
|
|
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtGui>
MainWindow::MainWindow(QWidget *parent):
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
for (int i = 0; i < maxR; ++i)
for (int j = 0; j<maxC; ++j)
Color[i][j] = QColor(0, 100, 100);
// Заполняем матрицу начальным цветом
previousRows = rows = columns = 0; // начальные размеры сетки
}
MainWindow::~MainWindow()
{
delete ui;
}
// Виртуальная функция, которая вызывается при изменении размеров окна
void MainWindow::resizeEvent(QResizeEvent *)
{
w = width(); // ширина рабочего поля
y0 = centralWidget()->y(); // Верхняя граница клиентской области
h = height() - y0; // высота рабочего поля
size = qMin(w, h) / 5;
// размер одной ячейки (расстояние между двумя противоположными вершинами)
if (size<2) size = 2;
// размер не может оказаться равным нулю, чтобы не произошло деление на ноль
// qDebug() << "size = " << size;
r = size/2; // радиус окружности, в которую вписана ячейка
x0 = r * sqrt(3)/2;
// расстояние от левой границы окна до центра первого 6-угольника (красная линия на рисунке)
/* длина стороны правильного 6-угольника = r, а расстояние между ценрами ячеек (вертикальный катет светлого треугольника) = r+r/2 = 3*r / 2 */
rows = int(2*h/(3*r)) +2; // количество полных рядов равно частному от деления высоты рабочей области на расстояние между центрами ячеек; к этому количеству нужно добавить 2 — неполные ряды на верхней и нижней границах окна
if (rows>maxR) rows = maxR;
// но количество рядов не может превышать задданый максимум
// qDebug() << "rows = " << rows;
columns = int(w / (r*sqrt(3)))+2;
/* расстояние между центрами окружностей по горизонтали = (r*sqrt(3)/2) * 2 = r*sqrt(3), количество столбцов равно частному от деления ширины окна на расстояние между рядами + 2 неполных ряда на левой и правой границе */
if (columns>maxC) columns = maxC;
// количество столбцов неможет превышать заданный максимум
// qDebug() << "columns = " <<columns;
createMatrices(); /* при каждом изменении размеров окна создаем новые матрицы ячеек и соответствующих им областей */
createNet(); // заполняем матрицы данными
}
void MainWindow::paintEvent(QPaintEvent *)
{
QPainter painter(this);
// рисуем многоугольники цветом, который записан в матрице цветов:
for (int i = 0; i < rows; ++i)
for (int j = 0; j < columns; ++j)
|
|
{
painter.setBrush(QBrush(Color[i][j]));
painter.drawPolygon(Polygon[i][j]);
}
}
// размещаем матрицы в памяти:
void MainWindow::createMatrices()
{
if (previousRows)
// удаляем предидущие матрицы из памяти, если они ненулевого размера
{
for (int i = 0; i < previousRows; ++i) // освобождаем память по строкам
{
delete[] Polygon[i]; // освобождаем память, выделенную для данных
delete[] Region[i];
}
delete Polygon; // удаляем массивы указателей
delete Region;
}
previousRows = rows; // запоминаем текущее количество строк
// создаем массивы указателей на строки данных:
Polygon = new QPolygon* [rows];
Region = new QRegion* [rows];
// захватываем память для строк данных:
for (int i = 0; i < rows; ++i)
{
Polygon[i] = new QPolygon[columns]; // для координат 6-угольников
Region[i] = new QRegion[columns]; // для областей
}
}
void MainWindow::createNet() // заполняем матрицы данными
{
double dx = r*sqrt(3); // расстояние между центрами ячеек по горизонтали
double dy = r*3 / 2; // расстояние между центрами ячеек по вертикали
for (int i = 0; i <rows; ++i) // проходим по строкам
{
for (int j = 0; j<columns; ++j) // проходим по одной строке
{
createPolygon(i, j);
// создаем ячейку в i-ой строке и в j — м столбце (вызываем функцию)
x0+=dx; // переходим в следующий столбец
}
y0 += dy; // переходим к следующей строке
if (i % 2 == 0) x0 = 0;
// начало следующего ряда, если текущая строка - четная
else x0 = r*sqrt(3)/2; // если текущая строка - нечетная
}
}
void MainWindow::createPolygon(int i, int j) // определяем координаты одной ячейки
{
double x, y; // координаты вершин многоугольника
double da = M_PI/3; // угол между вершинами
double angle = da/2; // начальный угол
Polygon[i][j].clear(); // очищаем контейнер
for (int k = 0; k<6; ++k) // определяем вершины 6-угольника
{
// вычисляем координаты очередной вершины как точки на окружности
x = x0 + r*cos(angle);
y = y0 + r*sin(angle);
Polygon[i][j] << QPoint(x,y);
// добавляем координаты очередной точки к полигону
angle += da; // переходим к следующей вершине
}
Region[i][j] = QRegion(Polygon[i][j]);
// когда 6-угольник определен, создаем соответствующую область
}
// Слот, обрабатывающий щелчки мышью:
void MainWindow::mousePressEvent(QMouseEvent * event)
{
QPoint point = event->pos(); // координаты курсора мыши
// проверяем, в какой регион (в какой 6-угольник) попадает курсор мыши
for (int i = 0; i<rows; ++i) // проходим по строкам матрицы
for(int j = 0; j<columns; ++j) // по столбцам
if (Region[i][j].contains(point)) // если курсор попадает в область
{
Color[i][j] = QColorDialog::getColor(); // выбираем цвет
brush = QBrush(Color[i][j]); // создаем кисть
update(Region[i][j]); // перерисовываем 6-угольник
goto exit; // выходим из циклов
}
exit:;
}
// слот для команды Save:
void MainWindow::on_actionSave_triggered()
{
// вызываем окно для выбора имени файла:
QString fileName = QFileDialog::getSaveFileName(this, "Сохранить файл", QString(),
QString("Файлы данных (*.dat);;Все файлы (*.*)"));
QFile rezFile; // Результирующий файл
if (!fileName.isEmpty()) // Если имя файла задано
{
rezFile.setFileName(fileName);
// присваиваем физическое имя результирующему файлу
rezFile.open(QIODevice::WriteOnly);
// открываем сохраняемый файл
QDataStream out(&rezFile);
/* создаем поток, связанный с файлом (запись файла обычно выполняется с помощью потока) */
// записываем матрицу цветов в файл:
for (int i = 0; i < maxR; ++i)
for (int j = 0; j<maxC; ++j)
out <<Color[i][j]; // записываем цвет ячейки с номерами i и j
rezFile.close();
}
}
// Слот, обрабатывающий команду Open:
void MainWindow::on_actionOpen_triggered()
{
// вызываем окно для выбора имени файла:
QString fileName = QFileDialog::getOpenFileName(
this, // указатель на родительское окно
"Открыть файл", // заголовок окна
QString(), // выбор начинается с текущего каталога
QString("Файлы с данными (*.dat);;Все файлы (*.*)"));
// фильтры для списка файлов
if (! fileName.isEmpty()) // если имя файла выбрано
{
QFile file;
file.setFileName(fileName);
// связываем объект file с физическим файлом
file.open(QIODevice::ReadOnly);
QDataStream in(&file); // поток для чтения данных
for (int i = 0; i < maxR; ++i)
for (int j = 0; j<maxC; ++j)
in >> Color[i][j]; // считываем цвет ячейки
file.close();
}
/* Главное окно приложения автоматически перерисовывается при закрытии окна для выбора файла */
}