ПЗ Шандова (1231575), страница 5
Текст из файла (страница 5)
Рисунок 3.12 – Окно создания новой таблицы
Таким образом была создана вся база данных. Программа MySQL Workbench справилась со своей задачей, не вызывая никаких затруднений в работе.
3.2 Приложение для ведения учета
3.2.1 Основные моменты работы с библиотекой Qt
Одним из ключевых механизмов библиотеки Qt является система сигналов и слотов. Этот механизм призван связывать события с реакцией на них. Такой подход применим ко всем классам, наследуемым от класса QObject. К слову, класс QObject является родительским абсолютно для всех классов библиотеки [22].
Связывание происходит при помощи метода QObject::connect(), в который передаются адреса источника и приемника сигнала, а также в специальных макросах SIGNAL() и SLOT() указываются сигнал и слот соответственно.
Например, имеется указатель ExitButton на объект класса QPushButton, реализующий кнопку в интерфейсе, и существует указатель MainWindow на объект класса QMainWindow, являющийся главным окном приложения. Теперь необходимо связать закрытие главного окна с нажатием на кнопку ExitButton. Связывание происходит следующим образом:
connect(ExitButton, SIGNAL(clicked()),MainWindow, SLOT(exit())).
Стоит отметить, что связывание происходит динамически, то есть не на этапе компиляции, а во время работы программы.
При наследовании класса от Qobject необходимо вписать макрос Q_OBJECT в раздел private определяемого класса, иначе во время сборки компилятор выдаст ошибку. Эта необходимость возникла из-за особого подхода разработчиков к проектированию платформы Qt. Дело в том, что библиотека расширяет стандарт C++, вводя в него свои специальные конструкции, призванные упростить разработку. В итоге при сборке исходный код не сразу попадает на вход компилятору C++. Сначала он проходит через особый метакомпилятор MOC, который преобразует нестандартные конструкции в верный C++ код.
3.2.3 Сборка драйвера MySQL
Взаимодействие библиотеки Qt с любой СУБД происходит через специальный драйвер. Qt поставляется со множеством драйверов баз данных, однако почти все они распространяются только в исходных кодах. Поэтому необходимо собрать драйвер для работы с СУБД MySQL [23].
Сборка осуществляется в режиме командной строки следующей серией команд:
cd %QTDIR%\src\plugins\sqldrivers\mysql
qmake –o Makefile "INCLUDEPATH+=%MYSQL%\include"
"LIBS+=%MYSQL%\lib\opt\libmysql.a" mysql.pro
mingw32-make
В приведенной выше строке кода: %QTDIR% – директория установки библиотеки Qt, а %MYSQL% – расположение сервера MySQL.
Результат компиляции (динамические библиотеки) помещается в каталог plugins/sqldrivers.
3.2.4 Обеспечение связи с базой данных
Для связи с базой данных был разработан класс, представленный ниже [24]:
class BD
{
public:
static bool conect();
static bool conect(const QString& s);
static void disconect();
static QSqlDatabase* db();
private:
static QSqlDatabase db_;
};
Поле db_ хранит экземпляр базы данных. Для настройки соединения используется методы conect. они предназначены для настройки соединения с базой данных. Их реализация представлена ниже.
bool BD::conect()
{
db_ = QSqlDatabase::addDatabase("QMYSQL");
db_.setDatabaseName("diploma");
db_.setHostName("localhost");
db_.setUserName("root");
db_.setPassword("1234");
bool r = db_.open();
qDebug() << "Conection error: " << db_.lastError().text();
return r;
}
bool BD::conect(const QString &s)
{
db_ = QSqlDatabase::addDatabase("QSQLITE");
db_.setDatabaseName(s);
bool r = db_.open();
qDebug() << "Conection error: " << db_.lastError().text();
return r;
}
Метод QSqlDatabase* db()предназначен для обеспечения доступа к соединению с базой данных.
Класс является статическим, поскольку необходимо обеспечить хранение единственного экземпляра соединения с сервером.
Непосредственное соединение с базой данных устанавливается во время создания главного окна программы.
3.2.5 Взаимодействие с таблицами
Для взаимодействия с каждой таблицей были реализованы модели. Все модели реализуют интерфейс QSqlQueryModel. Структура модели является стандартной и приведена на примере ниже:
class Orders : public QSqlQueryModel
{
Q_OBJECT
public:
Orders(QSqlDatabase* db, QObject *parent = 0);
~Orders();
bool insertRow(const QSqlRecord& );
bool editeRow (const QSqlRecord& );
bool deleteRow (int id);
bool update ();
private:
QString queryStrinq_, insertString_, editString;
QSqlDatabase* db_;
};
В полях queryStrinq_, insertString_, editString хранятся запросы для выборки, вставки и изменения записей таблицы.
Метод insertRow предназначен для вставки записей в модель.
QString t = " INSERT INTO orders values "
" ( ? , ?, ?, ? ,? ); " ;
QSqlQuery query(*db_);
query.prepare(t);
query.addBindValue(r.value("OrderDate").toDate());
query.addBindValue(r.value("Comments").toString());
query.addBindValue(r.value("Ready_NotReady").toBool());
query.addBindValue(r.value("Payment_ID").toInt());
query.addBindValue(r.value("Customer_ID").toInt());
return query.exec();
}
Метод editeRow предназначен для внесения изменений в таблицу.
bool Orders::editeRow(const QSqlRecord &r)
{
QString t = " update orders set "
" OrderDate = ?, "
" Comments = ?, "
" Ready_NotReady = ?, "
" Payment_ID = ?, "
" Customer_ID = ? "
" where OrderNumber = ?; " ;
QSqlQuery query(*db_);
query.prepare(t);
query.addBindValue(r.value("OrderDate").toDate());
query.addBindValue(r.value("Comments").toString());
query.addBindValue(r.value("Ready_NotReady").toBool());
query.addBindValue(r.value("Payment_ID").toInt());
query.addBindValue(r.value("Customer_ID").toInt());
query.addBindValue(r.value("OrderNumber").toInt());
return query.exec();
}
Метод deleteRow предназначен для удаления записи из таблицы.
bool Orders::deleteRow(int id)
{
QString t = "DELETE Orders WHEAR Orders.ID = ?";
QSqlQuery sq;
sq.prepare(t);
sq.addBindValue(id);
bool r = sq.exec();
if(!r)
qDebug() << "Orders delete error!" << sq.lastError();
return r;
}
Метод update предназначен для обновления содержимого модели из базы данных.
bool Orders::update()
{
setQuery(QSqlQuery(queryStrinq_, *db_));
setHeaderData(0,Qt::Horizontal ,"Номер заказа"); // добавить заголовки
setHeaderData(1,Qt::Horizontal ,"Дата заказа");
setHeaderData(2,Qt::Horizontal ,"Коментарий");
setHeaderData(3,Qt::Horizontal ,"Готовность");
setHeaderData(6,Qt::Horizontal ,"Название организации");
setHeaderData(7,Qt::Horizontal ,"Сумма к оплате");
setHeaderData(8,Qt::Horizontal ,"Дата оплаты");
if (lastError().type() == QSqlError::NoError)
return true;
qDebug() << "Orders tabel error!" << lastError().text();
return false;
}
3.2.6 Программирование интерфейса пользователя
Интерфейс пользователя состоит из главного окна приложения и диалоговых окон для просмотра/выбора дополнительной информации. Вызов диалоговых окон происходит через меню главного окна. Создание и вызов диалогов происходит посредством описания действий и задания слотов для них. Пример реализации такого слота приведен ниже:
void MainWindow::on_actPayment_triggered()
{
paymentdialog *dialog = new paymentdialog();
dialog->exec();
}
В диалоговых окнах присутствуют три типовые операции системы:
– вставка новой записи;
– редактирование выбранной записи;
– удаление выбранной записи.
Для реализации этих операций было создано три кнопки. Обработчики событий для них представлены ниже.
void productsdialog::on_pbadd_clicked()
{
QSqlRecord sqlRc = model_->record();
ProductEditeDialog *modelEdit = new ProductEditeDialog(sqlRc, this);
if(modelEdit->exec() == QDialog::Accepted) {
model_->insertRow(modelEdit->record());
model_->update();
}
}
void productsdialog::on_pbedit_clicked()
{
QSqlRecord sqlRc = model_->record(ui->tableView->currentIndex().row());
ProductEditeDialog *modelEdit = new ProductEditeDialog(sqlRc, this);
if(modelEdit->exec() == QDialog::Accepted) {
model_->editeRow(modelEdit->record());
model_->update();
}
}
void productsdialog::on_pbdelete_clicked()
{
QSqlRecord sqlRc = model_->record(ui->tableView->currentIndex().row());
model_->deleteRow(sqlRc.value(0).toInt());
model_->update();
}
Подобным образом реализуются элементы остальных диалогов интерфейса разрабатываемого приложения.
Для получения данных из диалога редактирования, в них реализован метод record.
QSqlRecord &ProductEditeDialog::record()
{
return srRecord;
4 ЭКОНОМИЧЕСКОЕ ОБОСНОВАНИЕ
4.1 Выбор метода расчета стоимости разработки
При создании программного продукта очень важно оценить его стоимость. Это тем более важно, если программный продукт является востребованным и имеет все шансы конкурентоспособно выйти на рынок программного обеспечения.
Объем исходных текстов программы, прежде всего, отражает трудоемкость и длительность разработки программного обеспечения и позволяет оценивать относительные характеристики производительности труда специалистов-разработчиков [27]. Объем программ можно разделить на две группы:
– группа, характеризующая объем исходных текстов программ, которые разрабатываются и анализируются программистом (это символы в исходном тексте программы на любых языках программирования; лексемы, объединяющие группы символов, имеющих общее смысловое содержание в тексте программы; операторы языка программирования уровня ассемблера; строки текста программы на языке программирования высокого уровня);
– группа, отражающая объем программы, размещаемой в реализующей ЭВМ (это байты, занятые текстом программы в памяти ЭВМ; команды или операции реализующей ЭВМ, составляющие текст программы в объектном коде; слова памяти, обусловленные структурой данной реализующей ЭВМ, используемые для хранения исполняемой программы и/или базы данных при функционировании программных средств) [28].
Объем программы, размещаемой на ЭВМ, влияет на характеристики и стоимость машин, которая зависит от необходимой памяти и производительности ЭВМ. Учитывая, что при разработке программы выбор ЭВМ не производился и конкретные требования к реализующей машине не предъявлялись, будут использоваться единицы первой группы. Программа разрабатывалась на языке высокого уровня C++. При его разработке были учтены такие современные рекомендации к структурному программированию как отсутствие условных и безусловных переходов, запись операторов в одну строку (за несущественными исключениями), линейный подход к программированию. Следовательно, за число операторов в программе можно взять число строк в программе. Однако и такой подход к подсчету стоимости программы довольно плохо отразит реальную сложность разработки с учетом квалификации программиста. Разумеется, если таковой расчет необходим до начала разработки, то количество строк кода возможно поможет оценить трудозатраты, но предсказать объем исходного кода, не приступая к разработке, достаточно сложная задача. В данном случае, когда программное приложение уже реализовано, гораздо проще и достовернее оценить затраты уже после окончания разработки, основываясь на практических данных нежели на подверженных ошибкам теоретических расчетах.
Исходя из выше сказанного, за исходные данные возьмем количество человеко-часов, затраченных на дипломный проект и среднюю заработную плату, предлагаемую программисту начальной квалификации.
4.2 Исследование рынка труда на предмет средней заработной платы программиста начальной квалификации.
Для информации использовался интернет ресурс [29]. В разделе «Резюме» при помощи встроенного в портал поиска были найдены подходящие кандидатуры. Требования, предъявляемые к кандидатам следующие:
– уверенное владение языком программирования высокого уровня C++;
– навыки работы с платформой Qt;
– знание основ теории реляционных баз данных;
– опыт работы с реляционными СУБД.
Итогом поиска стали несколько специалистов, владеющие необходимыми знаниями и навыками. Требуемая в резюме заработная плата в различных случаях составила от 20 до 30 тысяч рублей в месяц. Средняя заработная плата составила 25 тысяч рублей.















