Поможем написать учебную работу
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Цель работы:
Понять принципы разработки графических приложений в среде Visual C++. Познакомиться со свойствами и методами соответствующих классов.
Задание
Разработать графический редактор. Редактор должен обеспечить выполнение следующих действий:
Сконструируем основное меню редактора с пунктами Файл (Создать, Открыть, Сохранить, Сохранить как, Выход), Карандаш (цвет, толщина), Кисть (цвет), используя компонент menuStrip - главное меню.
Поместим на форму компоненты-диалоги: два сolorDialog для выбора цвета карандаша и кисти; openFileDialog и saveFileDialog для открытия и сохранения файлов. Задайте для двух последних фильтр (свойство Filter) на графические типы файлов:
Image Files(*.BMP;*.JPG;*.PNG)|*.BMP;*.JPG;*.PNG.
Создадим панель с кнопками для удобства выбора изображаемых фигур. Панель это компонент Panel со страницы Containers. Чтобы она автоматически располагалась вверху формы, задайте для неё свойство Dock в значение Top. Кнопки обычные компоненты Button. Чтобы изобразить на них рисунки, воспользуемся свойством Image,
которое позволяет выбрать графический файл для рисунка на кнопке. Можно воспользоваться существующими файлами ellipse.bmp, line.bmp, rect.bmp, а в случае их отсутствия создать свои (например, с помощью редактора Paint).
Для обеспечения возможности изображения фигур на экране и записи изображения в графический файл потребуется компонент pictureBox со страницы Common Controls. Задайте для него свойство Dock равным Fill, чтобы он автоматически растянулся и заполнил всё оставшееся свободное пространство формы.
Компонент pictureBox содержит свойство Image, которое является ссылкой на объект (если быть уж совсем точным, то дескриптором отслеживания в терминологии CLR, но для простоты будем в дальнейшем использовать слово «ссылка»). Класс Image содержит статический метод FromFile(), который создаёт новый объект Image из указанного графического файла и возвращает ссылку на него. Для сохранения же изображения в файл можно использовать метод Save(). Таким образом, для пунктов меню «Открыть» и «Сохранить как» обработчики будут выглядеть следующим образом:
//Обработчик меню "Открыть"
private: System::Void открытьToolStripMenuItem_Click(System::Object^ sender,
System::EventArgs^ e)
{
if ( openFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK ) {
if (pictureBox1->Image != nullptr) pictureBox1->Image->~Image();
pictureBox1->Image = System::Drawing::Image::FromFile(openFileDialog1->FileName);
Text = openFileDialog1->FileName;
}
}
//Обработчик меню "Сохранить как"
private: System::Void сохранитьКакToolStripMenuItem_Click(System::Object^ sender,
System::EventArgs^ e)
{
if ( saveFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK ){
pictureBox1->Image->Save(saveFileDialog1->FileName);
Text = saveFileDialog1->FileName;
}
}
Примечание. В обработчике пункта меню «Открыть» присутствует явный вызов деструктора для объекта Image. Хотя в CLR и имеется такая технология, как сборка мусора (garbage collection), явное уничтожение объектов представляется целесообразным для более быстрого освобождения ресурсов.
Для выбора цвета карандаша в соответствующем обработчике достаточно написать одну строчку:
penColorDialog->ShowDialog();
Аналогично сделайте и выбор цвета кисти.
Для рисования простых геометрических фигур используется класс Graphics. Создать объект класса Graphics, связанный с нашим Image, можно с помощью статического метода FromImage(). Вот некоторые методы класса Graphics:
Перечисленные методы в качестве первого аргумента требуют объект класса Pen (перо, карандаш) либо Brush (кисть). Вместо самого класса Brush обычно применяются его наследники SolidBrush (сплошная кисть), TextureBrush (текстурная кисть) и др. Для лучшего понимания приведём пример кода, рисующего закрашенный эллипс и поверх него эллипс-границу другого цвета:
System::Drawing::Graphics^ g =
System::Drawing::Graphics::FromImage(pictureBox1->Image);
System::Drawing::Pen^ pen =
gcnew System::Drawing::Pen(penColorDialog->Color);
System::Drawing::Brush^ brush =
gcnew System::Drawing::SolidBrush(brushColorDialog->Color);
g->FillEllipse(brush,30,30,20,10);
g->DrawEllipse(pen,30,30,20,10);
brush->~Brush();
pen->~Pen();
g->~Graphics();
pictureBox1->Invalidate(); //вызываем перерисовку
Хотя для результирующего приложения данный кусок кода, конечно, не нужен, но попробуйте всё же его вставить в обработчик на какой-нибудь пункт меню и выполнить. Должна обнаружиться интересная ситуация. Если вы вначале с помощью меню «Файл» - «Открыть» загрузите какой-нибудь графический файл, то всё работает нормально. Если же вы запустите обработчик с пустым pictureBox, то программа завершается с ошибкой.
Проблема заключается в том, что по умолчанию свойство Image компонента pictureBox содержит пустую ссылку (nullptr). Поэтому давайте напишем обработчик пункта меню «Создать», в котором создадим новый объект класса Image (точнее, Bitmap наследника от Image) и ссылку на него поместим в свойство Image:
//обработчик пункта меню "Создать"
private: System::Void создатьToolStripMenuItem_Click
(System::Object^ sender, System::EventArgs^ e) {
pictureBox1->Image =
gcnew System::Drawing::Bitmap(pictureBox1->Size.Width,
pictureBox1->Size.Height);
}
Теперь после нажатия на кнопку «Создать» наш предыдущий пример будет отрабатывать нормально.
Осталось самое трудное сделать рисование выбранной фигуры с помощью мышки. Для этого нужно написать три обработчика событий от мыши: MouseDown (при нажатии левой кнопки), MouseMove (перемещение мыши) и MouseUp (при отпускании нажатой ранее кнопки). При нажатии кнопки необходимо запомнить текущие координаты.
При движении мыши необходимо постоянно стирать и рисовать заново выбранную фигуру, чтобы возникло впечатление, что она изменяется в размерах. Для этого необходимо также запоминать текущие координаты, чтобы стирать фигуру, нарисованную при предыдущем перемещении.
Стирание фигуры очень интересный момент, поскольку при этом необходимо восстанавливать то, что было нарисовано ранее. Для этого применим следующий приём: рисовать фигуру будем обычным образом, а для стирания создадим отдельный карандаш на основе текстурной кисти (TextureBrush). В свою очередь, в конструктор TextureBrush передадим свойство Image из pictureBox. В результате использования такого карандаша точки линии будут рисоваться в соответствии с текстурой кисти, то есть рисунком, который находился в Image ещё до рисования. И ещё один момент. По умолчанию при применении текстурной кисти рисуемое изображение смешивается с уже имеющимся, а нам нужно, чтобы оно рисовалось взамен его. С этой целью нужно изменить свойство CompositingMode объекта Graphics, задав его равным SourceCopy.
Приведём упрощенный пример подобных обработчиков для рисования только лишь эллипсов. Вам нужно будет их доработать, чтобы можно было рисовать любую фигуру.
//координаты мышки в момент нажатия кнопки
static int startX, startY;
//координаты мышки при предыдущем перемещении
static int prevX, prevY;
//ссылки на некоторые объекты,чтобы всё время не пересоздавать
static System::Drawing::Graphics^ g = nullptr;
static System::Drawing::Pen^ pen = nullptr;
static System::Drawing::Pen^ erasePen = nullptr;
static System::Drawing::Brush^ brush = nullptr;
//нажата кнопка мышки - запоминаем координаты
private: System::Void pictureBox1_MouseDown(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) {
startX = prevX = e->X;
startY = prevY = e->Y;
g = System::Drawing::Graphics::FromImage(pictureBox1->Image);
//создаём обычный карандаш
pen = gcnew System::Drawing::Pen(penColorDialog->Color);
//создаём очищающий карандаш
System::Drawing::Brush^ brush = gcnew
System::Drawing::TextureBrush(pictureBox1->Image);
erasePen = gcnew System::Drawing::Pen(brush);
//режим рисования - замена старого изображения
g->CompositingMode =
System::Drawing::Drawing2D::CompositingMode::SourceCopy;
}
//перемещение мышки - перерисовываем фигуру
private: System::Void pictureBox1_MouseMove(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) {
if (g==nullptr) return;
//стираем нарисованное до этого
g->DrawEllipse(erasePen,startX,startY,prevX-startX,prevY-startY);
//и рисуем заново
g->DrawEllipse(pen,startX,startY,e->X-startX,e->Y-startY);
prevX=e->X; prevY=e->Y;
pictureBox1->Invalidate(); //вызываем перерисовку
}
//отпускание мышки - освобождаем ресурсы
private: System::Void pictureBox1_MouseUp(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) {
pen->~Pen(); pen=nullptr;
erasePen->Brush->~Brush();
erasePen->~Pen(); erasePen=nullptr;
g->~Graphics(); g=nullptr;
}
На данный момент редактор должен выглядеть примерно так:
Самостоятельные задания.