Міністерство освіти та науки України
Національний університет “Львівська політехніка”
Створення WIN32-програм з розділеними
паралельно у часі математичними обчисленнями
Інструкція до лабораторної роботи № 1
з курсу “Комп’ютерні методи дослідження систем керування”
для студентів базового напрямку 6.0914
“Комп’ютеризовані системи, автоматика і управління”
та базового напрямку 050201 “Системна інженерія”
Затверджено
на засiданнi кафедри
“Комп’ютеризовані
системи автоматики»
Протокол № 2 від 03.10.2007
Львів 2007
Створення WIN32-програм з розділеними паралельно у часі математичними обчисленнями: Інструкція до лабораторної роботи № 1 з курсу “Комп’ютерні методи дослідження систем керування” для студентів базового напрямку 6.0914 “Комп’ютеризовані системи, автоматика і управління” та базового напрямку 050201 “Системна інженерія” / Укл.: У.Ю. Дзелендзяк, А.Г. Павельчак, В.В. Самотий – Львів: НУЛП, 2007.- 32 с.
Укладачі: У.Ю. Дзелендзяк, к.т.н., доцент
А.Г. Павельчак, асистент
В.В. Самотий, д.т.н., професор
Відповідальний за випуск:
А.Й. Наконечний, д.т.н., професор
Рецензент: З.Р. Мичуда, д.т.н., професор
Мета роботи: отримати навики роботи у системі візуального об’єктно-орієнтованого програмування C++Builder та освоїти розроблення програм орієнтованих на виконання складних математичних розрахунків з розділенням їх виконання паралельно у часі.
1. Вступ
Розроблення алгоритмічних програм, призначених для вирішення певних складних математичних задач, вимагає від дослідника знання однієї з алгоритмічних мов, наприклад мови С++. Вивчити команди мови С++ та навчитися писати нею невеликі програми, наприклад обчислення суми двох чисел, можна достатньо швидко. Однак сама мова не містить засобів для організації взаємодії із користувачем. Більш того, у С++ немає ніяких засобів для створення вікон та елементів операційної системи Windows, хоча б тому, що й створювалася ця мова, коли Windows ще не було. Засоби, необхідні для організації простого користувацького інтерфейсу за допомогою С++, є виділені в окремі спеціальні бібліотеки, які містять багато найрізноманітніших функцій. Тому програмування вручну звичних для користувача вікон, кнопок, меню, обробки подій миші та клавіатури, відображення в програмі різних зображень вимагає від програміста великих затрат часу, а сам сервіс може займати до 90% від об’єму програмного коду.
Для спрощення написання програм з графічним інтерфейсом користувача (GUI – graphical user interface) було здійснено декілька підходів. Перший з них – стандартизація системних функцій та поява користувацького інтерфейсу Windows API (Application Programming Interface). WIN32 API – це набір функцій, структур, повідомлень, макросів та інтерфейсів, за допомогою яких можна створювати програми для будь-якої WIN32-платформи. Оскільки WIN32 API це набір С-функцій, то їх можна використовувати у своїй програмі, просто включивши у програмний код відповідний заголовний файл. Але навіть при використанні WIN32 API програмний код є достатньо громіздким, і для недосвідченого програміста це завдання є складним, і може зайняти більше часу, аніж поставлена перед ним математична задача. У 1992 році компанія Microsoft розробила об’єктно-орієнтовану інтерфейс-оболонку для Windows API у вигляді програмного продукту Application Frameworks (AFX), який у подальшому розвинувся у пакет Microsoft Foundation Classes (MFC). Класи MFC, що інкапсулюють основні фукнції WIN32 API, хоч і спрощують та прискорюють розробку Windows-програм, але на сьогодні вони вже вважаються застарілою технологією. Основним їх недоліком вважають відсутність підтримки компонентів, властивостей та подій.
Другим підходом до полегшення життя програмістам вважається поява візуального програмування на основі форм та компонентів, що виникло у Visual Basic. Спершу ця ініціатива швидко перехопилася фірмою Borland та розвинулася у програмному пакеті Delphi (що використовує компілятор Object Pascal), а згодом і у пакеті C++Builder (на основі компілятора С++). З 2000 року фірма Microsoft спробувала відібрати цю ініціативу назад, що виразилася у появі нової платформи Visual Studio.NET. Дані програмні пакети базуються на CASE-технології (Computer Aided Software Engineering – автоматизоване проектування програмного забезпечення) та дозволяють швидко та якісно здійснювати проектування користувацького інтерфейсу, зосереджуючи свою основну увагу на поставленій задачі.
2. Система візуального програмування C++Builder
В даній роботі ми коротко ознайомимося із принципами розроблення програм для WIN32-платформи на основі п’ятої версії C++Builder.
C++Builder – продукт фірми Borland (яка тепер належить фірмі Inprise), що базується на концепції швидкого розроблення програм (RAD – Rapid Application Development). Центральне місце програмного пакету належить VCL (Visual Component Library – Бібліотека візуальних конпонентів), що представляє собою об’єктно-орієнтовану бібліотеку сформовану на основі візуальних компонентів.
VCL-компоненти представляють собою об’єкти, які наділені певними властивостями (Properties), мають великий набір методів (Methods) для роботи з компонентами, а найголовніше – обробник системних подій (Events) від клавіатури, миші, таймера тощо. Компоненти поділяють на візуальні (кнопки, текстові вікна, стрічки, меню, випадаючі списки, таблиці і т.д.) та невізуальні, що виконують ті чи інші службові функції, наприклад таймер.
Спробуємо на простому прикладі пояснити сутність VCL-компонента як об’єкта. Уявіть собі звичайне яблуко (рис. 1). Воно наділене рядом властивостей таких як колір, смак, назву сорту тощо.
Для нашого об’єкта (яблука) є ряд методів (функцій), що виконують з ним деякі операції, наприклад, функція Knife() розрізає яблуко навпіл, а функція BlackBoy() заставляє хлопчика перенести яблуко з одного місця у інше. До того ж наше яблуко здатне реагувати на зовнішні події, наприклад, коли засвітить сонце, то яблуко поміняє свій колір із зеленого на червоний, коли покрапає дощик, то воно зів’яне, а коли хтось доторкнеться до нього – тоді із середини вилізе хробак та вкусить за палець. Разом взяті властивості, методи та обробники подій формують об’єктний тип даних, який називають класом. Класи в дечому схожі на структури, однак вони крім даних містять ще й набір функцій для роботи з ними. Детально про класи можна дізнатися у будь-якій книзі з алгоритмічної мови С++.
Подібну структуру мають і VCL-компоненти, для яких ми можемо змінювати їхні властивості (колір, ширину, шрифт, назву підключеної бази даних тощо), викликати їхні методи обробки (вставити текст із буферу обміну, конвертувати числа тощо) та здійснювати оброблення системних подій (одинарний натиск миші, подвійний натиск, натиск клавіш клавіатури, рух миші над компонентою, спрацювання таймеру тощо).
Тепер ознайомимося із інтегрованим середовищем розробника (IDE – Integrated Development Environment) C++Builder. При першому запуску програми на екрані буде присутня пуста форма та IDE, як показано на рис. 2.
Панель керування складається з: а – меню, б – панелі інструментів (зберегти, відкрити тощо) та в – палітри VCL-компонентів (вкладки – Standard, Addition, Win32, System і т.д.).
Макетувальна форма – це вікно Windows, у якому розміщують різноманітні елементи керування (кнопки, меню, перемикачі, списки, елементи вводу і т.д.). ЇЇ можна порівняти із дитячим конструктором LEGO. Чиста форма немов підкладка для майбутньої конструкції LEGO, а VCL-компоненти – це складальні кубики, шарніри, перегородки, вікна з яких конструюється задумана модель. Коли створена програма буде відкомпільована та запущена, форма перетвориться у звичайне вікно Windows та почне виконувати ті дії, які для неї є визначені.
Інспектор об’єктів призначений для встановлення значень властивостей VCL-компонентів (вкладка г) та визначення їх реакції на різноманітні події (вкладка д).
У вікні редактора вписується програмний код. Варто зазначити, що робота редактора є автоматизована: компоненти, які добавляються на формі, автоматично прописуються у програмі; при створені функції-обробника користувацької чи системної події автоматично формується заголовок функції, а користувачу залишається лише добавити у тіло функції програмний код. І навпаки, при видаленні компонента з форми – витирається відповідний програмний код оголошення компонента, а при витиранні тіла функції-обробника та зберіганні проекту – знищується автоматично і оголошення цієї функції.
Зауваження: ніколи не міняйте вихідний код програми, який був створений автоматично. Це може призвести до повної непрацездатності вашої програми.
Структура проекту програми C++Builder
По замовчанню структура програми складається з таких основних файлів:
– Project1.cpp це базовий файл, що містить основну функцію Windows-програми WinMain (для консольних програм – main). Він має приблизно такий вигляд:
//---------------------------------------------------------------------------
//директиви препроцесора
#include <vcl.h>
#pragma hdrstop
//макроси, що підключають файли ресурсів та форм
USERES("Project1.res");
USEFORM("Unit1.cpp", Form1);
//---------------------------------------------------------------------------
// основна функція main
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
try
{
Application->Initialize();
Application->CreateForm(__classid(TForm1), &Form1);
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
return 0;
}
//---------------------------------------------------------------------------
До чотирьох параметрів функції WinMain відносяться два дескриптора (HINSTANCE), вказівник на стрічку (LPSTR) та ціле значення (int). Дескриптор – це деякий унікальний вказівник, що визначає системний об’єкт для програми. Перший параметр – дескриптор основного екземпляра програми; другий – дескриптор попереднього екземпляра та в середовищі WIN32 вже не використовується і завжди рівний нулю; третій – вказівник на стрічку з нульовим символом в кінці, яка містить параметри, що передаються у програму через командну стрічку; четвертий – передається програмою у функцію ShowWindow(), де вказує поведінку вікна програми: поява у вигляді піктограми, у звичному вигляді чи розкритися на весь екран.
Блок try {..} catch {..} відповідає за оброблення виключних ситуацій, що виникають усередині тіла try.
Оператор Application->Initialize() ініціалізує об’єкти компонентів програми. Оператор Application->CreateForm() створює об’єкти нашої форми. А оператор Application->Run() починає виконання програми. При виникненні аварійних ситуацій усередині тіла try керування передається до блоку catch та запускається універсальний обробник виключень Application->ShowException().
Всі описані вище оператори файлу Project1.cpp створюються автоматично в процесі проектування програми, а сам файл приховується від розробника.
– Unit1.dfm файл запису параметрів проектованої форми вікна.
– Unit1.h заголовний файл для нашої форми. Фактично в ньому вміщується опис класу форми. При створенні чистої форми цей файл має такий вигляд:
//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//тут можуть підключатися додаткові файли та бібліотеки
//---------------------------------------------------------------------------
//оголошення класу форми TForm1
class TForm1 : public TForm
{
__published: // IDE-managed Components
//оголошення розміщених на формі компонентів
private: // User declarations
//закритий розділ класу
//тут можуть розміщуватися оголошення типів, змінних, функцій
//не доступних за межами об’єкту даного класу
public: // User declarations
//відкритий розділ класу
//тут можуть розміщуватися оголошення типів, змінних, функцій,
//що є доступними назовні
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
У розділах public та private можна розміщувати опис необхідних змінних чи функцій та отримувати до них доступ з будь-якого місця програмного коду, що відноситься до цієї форми. Наприклад, можемо розмістити на формі дві кнопки та натиском миші на них змінювати значення нашої змінної шляхом виклику функцій, що обробляють події миші для цих кнопок.
– Unit1.cpp файл реалізації модуля. Він має такий вигляд
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//тут можуть підключатися додаткові файли та бібліотеки
//наприклад #include <math.h>
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
//Оголошення об’єкта форми Form1
TForm1 *Form1;
//---------------------------------------------------------------------------
//виклик конструктора форми Form1
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
Даний файл формується автоматично. Нам лише залишається дописувати необхідний програмний код у тіла функцій-обробників системних та користувацьких подій.
Зауваження: при подвійному натисненні мишею у пустому полі інспектора об’єктів у вкладці Events (події) навпроти необхідної нам події, шапка функції, що реагує на дану подію, формується автоматично у кінці файлу Unit1.cpp.
– Project1.res файл, що містить ресурси проекту.
– Project1.bpr файл із записом опцій проекту. При подвійному натисненню по ньому запускається пакет C++Builder та відтворюються файли та форми даного проекту.
Перелічені вище файли є найважливішими та вміщують усю необхідну інформацію проекту програми. Однак під час компіляції та лінкування програми можуть створюватися додаткові файли. Деякі з них мають значний об’єм і тому іноді з метою економії місця їх можна видалити. Ось перелік цих додаткових файлів:
*.obj об’єктні файли модулів;
Project1.tds файл таблиці символів (для відлагодження);
*.~cpp, *.~dfm, *.~h, *.~bpr файли резервної копії.
Практичні заняття по створенню програм
з елементами користувацького інтерфейсу
Практичне заняття № 1. Запускаємо програмний пакет C++Builder. Після завантаження пакету вигляд на екрані монітору має бути схожим як на рис. 2. Наступним кроком зберігаємо «голий» проект форми вікна. Для цього вибираємо з меню опцію File | Save All або натискаємо піктограму на панелі інструментів (рис. 2б). Зберігаємо проект Project1.bpr та файли форми Unit1.cpp, Unit1.h, Unit1.dfm у довільному місці, звичайно найкраще перед тим створити для них нову папку. Не рекомендується зберігати проект на віддаленому комп’ютері чи сервері, вже краще зберегти на робочому столі Windows. Коли проект збережений, можемо скомпілювати проект та запустити програму на виконання, натиснувши піктограму зеленого трикутника або вибравши у меню Run | Run (F9).
У результаті наших дій ми отримаємо повноцінну Windows-програму у вигляді чистого вікна. Закриваємо отримане вікно та повертаємося до нашого проекту. Роботу по освоєнню пакету C++Builder розіб’ємо на 6 етапів. Після виконання кожного етапу ми зберігаємо наш проект та запускаємо на виконання програму .
Етап 1. Для зміни надпису на нашій формі заходимо у інспектор об’єктів (рис. 2) та вибираємо вкладку г Properties (Властивості). У інспекторі об’єктів серед переліку властивостей шукаємо Caption і справа від нього замість Form1 вписуємо, наприклад, Лабораторна робота №1. Переконуємося, що надпис вгорі форми змінився. Далі в інспекторі об’єктів переключаємося на вкладку д Events (Події) та вибираємо мишею назву події OnClick. Двічі натискаємо мишею справа від цієї події, і як результат, програмна оболонка автоматично переключає нас у вікно редактора програмного коду та вписує у кінець файлу пусту функцію для оброблення події одинарного натиску мишею по формі.
void __fastcall TForm1::FormClick(TObject *Sender)
{
}
Наше завдання лише дописати програмний код у тіло функції. Спробуємо поміняти кольори форми при натиску мишею по ній. Для цього впишіть у цю функцію такий код
void __fastcall TForm1::FormClick(TObject *Sender)
{
if (Tag==0)
{Color=clRed; Tag=1; return;}
if (Tag==1)
{Color=clGreen; Tag=2; return;}
if (Tag==2)
{Color=clYellow; Tag=0; return;}
}
Зберігаємо наш проект та запускаємо на виконання програму . Після зібрання програми ми отримаємо знову наше вікно із надписом вгорі Лабораторна робота №1. Натискаємо мишею по вікну. Форма циклічно міняє кольори на червоний, зелений, жовтий.
Етап 2. Згідно рис. 3 розміщуємо на формі два компоненти: Label (мітка) та Button (кнопка) . Для їх розміщення необхідно спершу натиснути мишею по вибраному нами зображенню компонента у палітрі Standard (рис. 2в), а потім ще раз на формі, де ми хочемо його розмістити.
Вибираємо мишею на формі зображення кнопки та, аналогічно як на етапі 1, активізуємо в інспекторі об’єктів (у вкладці Events) подію OnClick. Цього самого можна добитися і простіше: потрібно вибрати зображення кнопки на формі та двічі натиснути мишею по ній. У автоматично створену функцію-обробник подій внесемо такий програмний код
void __fastcall TForm1::Button1Click(TObject *Sender)
{
if (Label1->Tag==0)
{Label1->Caption="Помідор"; Label1->Tag=1; return;}
if (Label1->Tag==1)
{Label1->Caption="Огірок"; Label1->Tag=2; return;}
if (Label1->Tag==2)
{Label1->Caption="Капуста"; Label1->Tag=0; return;}
}
Зберігаємо наш проект та запускаємо на виконання програму . У зібраній програмі натискаємо мишею по кнопці та спостерігаємо зміну тексту на розміщеній вище мітці: Помідор, Огірок, Капуста.
Етап 3. Згідно рис. 3 розміщуємо на формі такі три компоненти: Memo (текстове вікно) , Edit (стрічка) Button (кнопка) . Текстове вікно (Memo) розтягніть до потрібних вам розмірів. Для цього виділіть на формі зображення цього компонента та потягніть мишею за краї. Тепер зайдіть у інспектор об’єктів у вкладку Properties (Властивості) та для ScrollBars втановіть значення ssVertical. Подвійним натиском миші по зображенню кнопки активізуємо функцію-обробник події OnClick та внесемо до неї такий код
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Memo1->Lines->Add(Edit1->Text);
}
Зберігаємо наш проект та запускаємо на виконання програму . У зібраній програмі вписуємо довільний текст в Edit (стрічку) та натискаємо запрограмовану кнопку. В результаті ми добавляємо вміст стрічки до текстового вікна. Змініть напис у стрічці та за допомогою кнопки внесіть знову його до текстового вікна. Якщо повторити цю операцію значну кількість разів, аж поки не заповниться уся видима частина текстового вікна (Memo), то тоді стане активним вертикальний повзунок прокрутки.
Етап 4. Згідно рис. 3 розміщуємо на формі три компоненти Label (мітка) , два компоненти Edit (стрічка) , один компонент Panel (панель) та один Button (кнопка) . Для першої мітки замінюємо у інспекторі об’єктів значення властивості Caption на Число 1, для другої мітки – Число 2, для третьої мітки (на рис. 3 вона позначена як Label4) – Результат. Для компоненти Button (кнопка) встановлюємо значення Caption Розрахунок. Потім подвійним натиском миші по зображенню кнопки активізуємо функцію-обробник події OnClick та внесемо до неї такий код
void __fastcall TForm1::Button3Click(TObject *Sender)
{
double m;
m=Edit2->Text.ToDouble() * Edit3->Text.ToDouble();
Label4->Caption="Результат" + Edit2->Text + "*" + Edit3->Text;
Panel1->Caption=m;
}
Програмна стрічка Edit2->Text.ToDouble() викликає для компоненти Edit метод ToDouble(), який конвертує значення стрічкового типу властивості Text цієї компоненти у число з плаваючою комою типу double.
Зберігаємо наш проект та запускаємо на виконання програму . Спершу внесемо довільні числа у поля стрічок, а потім натискаємо запрограмовану кнопку. Якщо ж у дані стрічки не вписати числа, або ж вписати їх невірно, наприклад, використати для розділення дробової частини кому замість крапки, або навпаки, тобто не дотримуючись регіональних налаштувань системи, то програма видасть відповідне повідомлення про помилку.
Етап 5. Для нашої програми створимо основне меню. Для цього розмістимо на формі компонент MainMenu (основне меню) . Двічі натиснемо мишею по зображенню меню на формі. Перед нами з’явиться конструктор меню.
Мишею вибираємо верхній пустий прямокутник (на рис. він вже має надпис Меню). У інспекторі об’єктів для його властивості Caption вписуємо значення Меню та тиснемо клавішу вводу Enter ( . Далі вибираємо наступну у конструкторі комірку та встановлюємо її властивість Caption у значення Закрити. Двічі натискаємо мишею у конструкторові меню по створеному надпису Закрити, тим самим викликаючи функцію-обробник події OnClick, та внесемо у тіло цієї функції такий програмний код
void __fastcall TForm1::N1Click(TObject *Sender)
{
Close();
}
Зберігаємо наш проект та запускаємо на виконання програму . Заходимо мишею у меню та вибираємо вкладку Закрити. У результаті – наша програма-вікно буде завершена.
Етап 6. Згідно рис. 3 розміщуємо на формі компонент PageControl (вкладки зі сторінками) , що знаходиться на палітрі VCL-компонентів у вкладці WIN32. В результаті на формі з’явиться прямокутник. Натискаємо по ньому правою кнопкою миші та вибираємо зі спливаючого меню опцію New Page. Цю операцію повторюємо ще раз та отримуємо ще одну вкладку. Перемикаємось у компоненті на першу сторінку та розміщуємо на ній підряд три компоненти RadioButton (перемикач) . Для цих компонентів у інспекторові об’єктів змінюємо властивості Caption на довільні значення. Далі для кожного перемикача подвійним натиском миші активізуємо функції-обробники події OnClick та внесемо до них такі програмні коди
для першого перемикача
void __fastcall TForm1::RadioButton1Click(TObject *Sender)
{
Caption="Ля-ля";
}
для другого перемикача
void __fastcall TForm1::RadioButton2Click(TObject *Sender)
{
Caption="Лю-лю";
}
для третього перемикача
void __fastcall TForm1::RadioButton3Click(TObject *Sender)
{
Caption="Ку-ку";
}
Зберігаємо наш проект та запускаємо на виконання програму . При запуску програми надпис на верхній смужці вікна є Лабораторна робота №1. Натискаємо по першому перемикачу та фіксуємо зміну назви вікна на Лю-лю. Потім натискаємо по другому та третьому перемикачах і кожного разу спостерігаємо зміну назви вікна.
Зауваження: для того щоб створена програма не вимагала файлів *.bpl, *.dll необхідно зайти у меню Project->Options та на вкладці Packages зняти галочку з "Build with runtime packages", а на вкладці Linker зняти галочку з опції "Use dynamic RTL".
Практичне заняття № 2. Перезапускаємо програмний пакет C++Builder. Після завантаження пакету зберігаємо чистий проект .
Згідно рис. 4 розміщуємо на формі три компоненти StringGrid (стрічкова решітка) , які знаходяться на вкладці Additional палітри VCL-компонентів. Виділяємо перший компонент StringGrid та в інспекторі об’єктів на вкладці Properties (Властивості) розкриваємо плюсик групи властивостей Options і встановлюємо властивість GoEditing у значення true. Аналогічно виставляємо і для другого компонента StringGrid властивість GoEditing у значення true.
У палітрі VCL-компонентів знову переходимо на вкладку Standard. Компонент Panel (панель) розміщуємо у правій верхній частині форми. Безпосередньо на зображені панелі розміщуємо два компоненти Edit (стрічка) та три компоненти Button (кнопка) . Для компонентів Button у інспекторові об’єктів встановлюємо значення властивостей Caption відповідно до рис. 4.
Для першої кнопки «Встановити розмір» подвійним натиском миші активізуємо функцію-обробник події OnClick та внесемо код
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int m,n;
m=Edit1->Text.ToInt()+1; n=Edit2->Text.ToInt()+1;
StringGrid1->RowCount=m; StringGrid1->ColCount=n;
StringGrid1->Height=25*m+10; StringGrid1->Width=65*n+10;
StringGrid2->RowCount=m; StringGrid2->ColCount=n;
StringGrid2->Height=25*m+10;
StringGrid2->Width=65*n+10;
StringGrid3->RowCount=m; StringGrid3->ColCount=n;
StringGrid3->Height=25*m+10; StringGrid3->Width=65*n+10;
for (int j=0;j<m; j++)
{
StringGrid1->Cells[0][j]=j;
StringGrid2->Cells[0][j]=j;
StringGrid3->Cells[0][j]=j;
}
for (int i=0;i<n; i++)
{
StringGrid1->Cells[i][0]=i;
StringGrid2->Cells[i][0]=i;
StringGrid3->Cells[i][0]=i; //продовження на стр.17
}
Panel1->Left=Width - Panel1->Width - 15; Panel1->Top=5;
StringGrid1->Left=5; StringGrid1->Top=5;
StringGrid2->Left=5; StringGrid2->Top=Height - StringGrid2->Height - 40;
StringGrid3->Left=Width - StringGrid3->Width - 15;
StringGrid3->Top=Height-StringGrid3->Height-40;
}
Коментар: перша частина коду функції-обробника здійснює зчитування розмірів матриць та встановлює візуальні розміри компонентів StringGrid. Середня частина програмного коду, що складається з двох циклів for, встановлює вертикальну та горизонтальну нумерацію комірок таблиць. Третя частина коду розсуває таблиці та панель по краях форми.
Зберігаємо наш проект та запускаємо на виконання програму . Перед натиском запрограмованої кнопки необхідно попередньо встановити розмір матриць. Для цього, наприклад, внесемо в обидва компоненти Edit (стрічка) двійку. Якщо ж не задати розмір матриць, то програма видасть інформаційне вікно про помилку конвертування.
Для другої кнопки «Додати матриці» подвійним натиском миші активізуємо функцію-обробник події OnClick та внесемо до неї такий програмний код
void __fastcall TForm1::Button2Click(TObject *Sender)
{
int m, n;
double dd;
m=Edit1->Text.ToInt(); n=Edit2->Text.ToInt();
for (int j=0;j<m; j++)
for (int i=0;i<n; i++)
{
dd=StringGrid1->Cells[i+1][j+1].ToDouble()
+StringGrid2->Cells[i+1][j+1].ToDouble();
StringGrid3->Cells[i+1][j+1]=dd;
}
}
Зберігаємо наш проект та запускаємо на виконання програму . Перед натиском запрограмованої кнопки сумування двох матриць необхідно спершу задати розмір матриць та внести у перші дві матриці довільні числа. Результат сумування виводиться у третю матрицю.
Для третьої кнопки «Перемножити матриці» подвійним натиском миші активізуємо функцію-обробник події OnClick та внесемо до неї такий програмний код
void __fastcall TForm1::Button3Click(TObject *Sender)
{
int m, n;
double dd;
m=Edit1->Text.ToInt(); n=Edit2->Text.ToInt();
for (int i=0;i<m; i++)
for (int j=0;j<n; j++)
{ dd=0;
for (int k=0;k<n; k++)
{
dd+=StringGrid1->Cells[k+1][i+1].ToDouble()
*StringGrid2->Cells[j+1][k+1].ToDouble();
StringGrid3->Cells[j+1][i+1]=dd;
}
}
}
Зберігаємо наш проект та запускаємо на виконання програму . Перед натиском запрограмованої кнопки для множення матриць їхній заданий розмір має бути пропорційним, наприклад 2х2 чи 4х4. В іншому випадку програма видасть інформацію про помилку.
Практичне заняття №3. Перезапускаємо програмний пакет C++Builder. Після завантаження пакету зберігаємо чистий проект .
Згідно рис. 5 розміщуємо на формі такі компоненти: з вкладки палітри VCL-компонентів Standard чотири компоненти Button (кнопка) , з вкладки Dialogs – компонент ColorDialog (діалог кольорів) , а з вкладки System – компонент Timer (таймер) та компонент PaintBox (коробка фарб) . Останній компонент розтягуємо якомога ширше на формі. Для компонента Timer (таймер) у інспекторі об’єктів встановлюємо для властивості Enabled значення false, а для властивості Interval значення 10.
На початку файлу Unit1.cpp підключаємо математичну бібліотеку
#include "math.h"
Для першої кнопки «Надпис» подвійним натиском миші активізуємо функцію-обробник події OnClick та внесемо до неї такий програмний код
void __fastcall TForm1::Button1Click(TObject *Sender)
{
PaintBox1->Canvas->TextOut(5,5,"Привіт Васю!!!");
}
Зберігаємо наш проект та запускаємо на виконання програму . Натискаємо запрограмовану кнопку «Надпис».
Для другої кнопки «Функція» подвійним натиском миші активізуємо функцію-обробник події OnClick та внесемо до неї такий програмний код
void __fastcall TForm1::Button2Click(TObject *Sender)
{
PaintBox1->Canvas->MoveTo(0,0);
for (int i=0;i<200;i++)
PaintBox1->Canvas->LineTo(i*3,200+50*sin(i*0.1));
}
Зберігаємо наш проект та запускаємо на виконання програму . Натискаємо запрограмовану кнопку «Функція».
Мишею вибираємо на формі зображення таймера та у інспекторові об’єктів у вкладці Events (Події) подвійним натиском миші активізуємо функцію-обробник події OnTimer та внесемо до неї такий програмний код
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
PaintBox1->Canvas->FillRect(Rect(0,0,Tag,Tag));
Tag++;
if (Tag==200)
{Tag=0; PaintBox1->Color-=0x3000;}
}
Після цього для третьої кнопки «Квадрат» подвійним натиском миші активізуємо функцію-обробник події OnClick та внесемо до неї такий програмний код
void __fastcall TForm1::Button3Click(TObject *Sender)
{
PaintBox1->Color=clYellow;
Timer1->Enabled=!Timer1->Enabled;
}
Зберігаємо наш проект та запускаємо на виконання програму . Натискаємо запрограмовану кнопку «Квадрат».
Для четвертої кнопки «Колір» подвійним натиском миші активізуємо функцію-обробник події OnClick та внесемо до неї такий програмний код
void __fastcall TForm1::Button4Click(TObject *Sender)
{
ColorDialog1->Color = PaintBox1->Color;
if (ColorDialog1->Execute())
PaintBox1->Color = ColorDialog1->Color;
}
Зберігаємо наш проект та запускаємо на виконання програму . Натискаємо кнопку «Квадрат», а далі натискаємо кнопку «Колір», вибираємо з палітри кольорів інший колір та тиснемо OK.
N.B. Після виконання цих трьох практичних занять ви отримаєте загальне поняття про роботу в програмному середовищі C++Builder та зможете складати прості Windows-програми з графічним інтерфейсом користувача для розв’язування ваших подальших математичних задач. Для вдосконалення своїх навиків та знань при програмування у C++Builder ви змушені самостійно досліджувати можливості пакету, користуючись при цьому його довідкою та відповідною тематичною літературою.
Зауваження: у VCL-компонентах та формі властивість Tag виконує роль числової закладки типу int.
3. Багатозадачність у Windows-програмах
Основною рисою сучасних операційних систем є багатозадачність. Кожній програмі системою періодично виділяється квант часу, протягом якого вона виконує свою ділянку коду. Не зважаючи на стан програми, після закінчення цього кванту система забирає керування від програми та передає його іншій програмі. Якщо програма зависла, то система від цього не постраждає. Керування у будь-якому випадку буде передано іншій програмі.
В операційній системі Windows реалізовані два види багатозадачності – процесна та потокова. Процес (Process) – це статичний об’єкт (програма), який не виконується, а попросту «володіє» виділеним йому адресним простором, іншими словами, процес є структурою у пам’яті. Випадок, при якому програма може вирватися із рамок свого процесу та пошкодити чужі ресурси, є практично неможливим. В адресному просторі процесу знаходяться не тільки код та дані, але й потоки (Thread) – об’єкти, що виконується. Саме потокам операційна система виділяє кванти часу, а не процесам. При запуску процесу автоматично запускається потік (який називається головним). При зупинці головного потоку автоматично зупиняється і процес. А так як процес без потоку попросту займає ресурси, то система автоматично знищує його. Поряд із первинним потоком можуть у процесі існувати й додаткові потоки, яким система виділятиме окремі кванти часу. Такий багатопотоковий підхід дає можливість в межах однієї програми здійснювати різні задачі: робота з портами, файлами, математичні обчислення тощо. Наприклад, програма Microsoft Word може одночасно корегувати граматику та друкувати, при цьому здійснюючи ввід даних з клавіатури.
Зупинимося на одній деталі. На однопроцесорному комп’ютерові у кожний конкретний момент часу виконується одна задача. Якщо при запуску двох-трьох невеликих програм часова затримка суб’єктно не помітна, то при запуску декількох програм, що потребують колосальних ресурсів, затримка при виконанні програм стає достатньо помітною. На багатопроцесорних системах за кожним процесором може бути закріплений свій потік, і тому на таких системах виконання програм здійснюється дійсно в багатозадачному режимі.
На основі програмного пакету C++Builder потокова багатозадачність може бути реалізована:
з використанням компоненти типу TThread;
засобами інтерфейсу WIN32 API.
А) Організація потокової багатозадачності за допомогою компоненти типу TThread.
Запускаємо програмний пакет C++Builder. Після завантаження пакету зберігаємо чистий проект . На формі розміщуємо 2 компоненти: Memo (текстове вікно) та Button (кнопка) .
Для того щоб додати компоненту потоку у свою програму вибираємо у меню команду File | New і у вікні Депозитарію на сторінці New вибираємо піктограму Thread Object. Після чого з’явиться вікно у якому потрібно ввести назву потоку. Вкажіть будь-яке ім’я, (наприклад, potiс1) і у ваш проект добавиться новий модуль з двох файлів: Unit2.cpp та Unit2.h.
файл реалізації Unit2.cpp
#include <vcl.h>
#pragma hdrstop
#include "Unit2.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
// Важливо: методи та властивості об’єктів VCL можуть використовуватися
// тільки с використанням методу, який викликається методом Synchronize
//
// Synchronize(UpdateCaption);
//
// де метод UpdateCaption може мати вигляд:
//
// void __fastcall Unit2::UpdateCaption()
// {
// Form1->Caption = "Updated in a thread";
// }
//---------------------------------------------------------------------------
__fastcall potic1::potic1(bool CreateSuspended)
: TThread(CreateSuspended)
{
}
//---------------------------------------------------------------------------
void __fastcall potic1::Execute()
{
//---- Тут розміщується код потоку ----
}
Функція Execute() є основною функцією потоку. Після закінчення її виконання завершується і виконання цього потоку програми. У цю функцію можна безпосередньо вписувати оператори виконання, виклик інших функцій тощо. Але якщо функція повинна працювати з VCL-компонентами чи звертатися до форми, то необхідно бути обережним, оскільки, як правило, можуть виникнути конфлікти між паралельно запущеними потоками. У цьому випадку у функції Execute() потрібно викликати метод Synchronize(), як показано це у коментарю файлу Unit2.cpp.
Давайте спробуємо запрограмувати цей потік для виконання обчислень. Спершу впишемо у тіло функції Execute() такий код
void __fastcall potic1::Execute()
{
for (count=0; count <=10000; count ++)
Synchronize(Calculation);
Synchronize(EndCalculation);
}
У кінці файлу Unit2.cpp запишемо функції, що викликаються методом Synchronize()
void __fastcall potic1::Calculation(void)
{
Form1->Memo1->Lines->Add(count);
}
void __fastcall potic1::EndCalculation(void)
{
Form1->Memo1->Lines->Add("обчислення всередині потоку завершено");
}
Для того, щоб потік «бачив» компоненти нашої форми необхідно підключити йому файл Unit1.h. Тому допишемо у самому верху файлу Unit2.cpp таку стрічку
#include "Unit1.h"
Тепер пропишемо оголошення функцій Calculation(), EndCalculation() та змінної count у файлі Unit2.h. Для цього у редакторові коду при відкритому файлі Unit2.cpp натискаємо праву кнопку миші та у спливаючому меню вибираємо команду Open Source / Header File або просто тиснемо гарячу клавішу Ctrl+F6. У заголовному файлі Unit2.h після оператора public: вписуємо оголошення функцій та змінної
public:
void __fastcall Calculation(void);
void __fastcall EndCalculation(void);
int count;
Повертаємося до файлу Unit1.cpp. У самому верху цього файлу підключаємо файл Unit2.h
#include "Unit2.h"
У верхній частині цього ж файла Unit1.cpp прописуємо після
TForm1 *Form1;
глобальний покажчик для нашого потоку
potic1 *pp1;
Переходимо до форми та подвійним натиском миші по зображенню кнопки активізуємо функцію-обробник події OnClick та внесемо до неї такий код
void __fastcall TForm1::Button1Click(TObject *Sender)
{
pp1=new potic1(false);
pp1->FreeOnTerminate = true;
}
Перша стрічка тіла функції створює наш потік з параметром false та одразу ж і запускає його. Якщо при створені потоку вказати параметр true, то тоді наш потік лише створиться, але не запуститься. Для його запуску необхідно буде викликати метод Resume().
Друга стрічка тіла функції встановлює параметр FreeOnTerminate нашого потоку у значення true. Це означає, що потік буде знищений після завершення своєї роботи.
Призупинення роботи потоку здійснюється методом Suspend().
Добавимо на форму ще один компонент Button (кнопка) та подвійним натиском миші по зображенню кнопки активізуємо функцію-обробник події OnClick і внесемо до неї такий код
void __fastcall TForm1::Button2Click(TObject *Sender)
{
if(Button2->Tag==0)
{ pp1->Resume(); Button2->Tag=1; }
else
{ pp1->Suspend(); Button2->Tag=0; }
}
Зберігаємо наш проект та запускаємо на виконання програму . Натискаємо кнопку №1. Спостерігаємо за роботою створеного нами потоку, що виводить результати своїх обчислень у текстове вікно. Пробуємо мишею переміщати вікно по екрані монітору. Вікно переміщається і водночас виводяться результати. Натискаємо кнопку №2. Вивід результатів припиняється. Натискаємо знову цю ж кнопку №2. Вивід результатів продовжується. Таким чином ми можемо у будь-який час призупинити виконання обчислень та в потрібний момент знову відновити.
Б) Організація потокової багатозадачності за допомогою засобів інтерфейсу WIN32 API.
У операційній системі Windows для створення нових потоків у адресному просторі батьківського процесу використовується функція CreateThread(). Ця функція повертає дескриптор потоку або NULL у випадку неможливості його створення.
HANDLE CreateThread (
LPSECURITY_ATTRIBUTES lpThreadAttributes, // вказівник на структуру
// захисту потоку
DWORD dwStackSize, // початковий розмір стеку потоку у байтах
LPTHREAD_START_ROUTINE lpStartAddress, //вказівник на функцію потоку
LPVOID lpParameter, // аргумент функції потоку
DWORD dwCreationFlags, // прапори створення потоку
LPDWORD lpThreadId // вказівник на змінну, якій буде присвоєний
// ідентифікатор потоку
);
На простому прикладі продемонструємо створення потоків та роботу з ними за допомогою функцій WIN32 API.
Запускаємо програмний пакет C++Builder. Після завантаження пакету зберігаємо чистий проект . На формі розміщуємо 2 компоненти Label (мітка) та 2 компоненти Button (кнопка) .
Переходимо до редактора програмного коду та у самому верху файлу Unit1.cpp підключаємо бібліотеку потокового вводу-виводу
#include<fstream.h>
У верхній частині цього ж файла прописуємо після
TForm1 *Form1;
глобальні дескриптори потоків та оголошення їхніх функцій
HANDLE Thread1, Thread2; // оголошення дескрипторів потоків
DWORD ThreadFunc1(LPVOID lParam); // оголошення потокових функцій
DWORD ThreadFunc2(LPVOID lParam);
У кінці файлу Unit1.cpp записуємо тіла потокових функцій
DWORD ThreadFunc1(LPVOID lParam)
{
fstream file;
file.open("process1.txt",ios_base::out | ios_base::trunc);
Form1->Label1->Caption="Процес1 запущено";
for (int i=0;i<100000;i++)
file<<i<<endl;
file.close();
Form1->Button1->Tag=0;
Form1->Label1->Caption="Процес1 завершено!";
}
DWORD ThreadFunc2(LPVOID lParam)
{
fstream file;
file.open("process2.txt",ios_base::out | ios_base::trunc);
Form1->Label2->Caption="Процес2 запущено";
for (int i=100000;i>0;i--)
file<<i<<endl;
file.close();
Form1->Button2->Tag=0;
Form1->Label2->Capti...