Разработка клиентской части приложения информационной системы торговой организации с помощью Qt
В рамках учебного курса «Средства визуального программирования приложений» (ИПКиПК) мы познакомились с Qt - кросс-платформенным инструментарием разработки ПО на языке программирования C++. Этап написания курсовой работы состоял из двух частей. В первой части, на занятиях по управлению базами данных мы разработали структуру базы данных MySQL и различные запросы, согласно своему варианту курсовой работы. Во второй части, мы разработали клиентскую часть приложения на Qt с использованием среды разработки Qt Creator и средой разработки графического интерфейса Qt Designer.
Теоретическая основа разработки автоматизированной информационной системы
В теоретической части курсовой работы рассмотрены основы разработки автоматизированной информационной системы: процесс создания, проектирования и использования базы данных MYSQL, реализация различных классов при разработке приложений с использованием библиотеки Qt.
Проектирование и разработка клиентского приложения информационной системы торговой организации
В практической части курсовой работы реализована структура базы данных торгового предприятия и описан процесс создания клиентского приложения с использованием программы Qt.
Проектирование базы данных торговой организации начиналось с создания всех нужных таблиц в базе, всех полей входящих в каждую таблицу, взаимодействия таблиц между собой с помощью специальных отношений (один ко многим, многие ко многим) и создание в соответствии с этими параметрами первичных и вторичных ключей.
Структура базы данных информационной системы торговой организации (файл ipk2012.sql):
I. Торговая точка (trade)
1. Номер точки (trade_id)
2. Тип торговой точки (универмаг, магазин, киоск) (trade_type)
3. Число торговых залов (trade_number)
4. Платежи за аренду (стоимость) (trade_rent)
5. Коммунальные услуги (стоимость) (trade_utilities)
II. Продавец (seller)
1. Номер (seller_id)
2. Фамилия (seller_family)
3. Имя (seller_name)
4. Оклад (seller_oklad)
5. Код торговой точки (trade_id)
III. Заявка (application)
1. Номер заявки (application_id)
2. Наименование товара (application_description)
3. Количество (application_sum)
4. Стоимость (application_cost)
5. Дата заявки (application_date)
5. Код товара (catalog_id)
6. Код поставщика (distributor_id)
IV. Поставщик (distributor)
1. Номер (distributor_id)
2. Категория (фирма, дилер) (distributor_category)
3. Адрес (distributor_adress)
4. Название (distributor_name)
V. Покупатель (buyer)
1. Номер покупателя (buyer_id)
2. Имя покупателя (buyer_name)
3. Фамилия покупателя (buyer_family)
4. Тип (частное или физическое лицо) (buyer_type)
VI. Продажи (sales)
1. Номер покупки (sales_id)
2. Дата покупки (sales_date)
3. Количество единиц (sales_sum)
4. Цена (sales_price)
5. Код товара (catalog_id)
6. Код продавца (seller_id)
7. Код покупателя (buyer_id)
VII. Справочник товаров (номенклатура, склад) (catalog)
1. Номер товара (catalog_id) - первичный
2. Название товара (catalog_name)
3. Количество (catalog_sum)
4. Цена (catalog_price)
VIII. Распределение (raspredelenie)
1. Код товара (catalog_id)
2. Код точки (trade_id)
Разработка клиентского приложения в Qt Creator
В библиотеке Qt есть отдельный модуль, предоставляющий удобный функционал использования базы данных — QtSql.
К уровню драйверов относятся классы для получения данных на физическом уровне, такие, как:
- QSqlDriver;
- QsqlDriverCreator <T*>;
- QSqlDriverCreatorBase;
- QSqlDriverPlugin;
- QSqlResult.
QSqlDriver является абстрактным базовым классом, предназначенный для доступа к специфичным БД. Класс не должен быть использован «прямо», взамен нужно воспользоваться QsqlDatabase. Для создания собственного драйвера SQL можно наследовать функции от QSqlDriver и реализовать необходимые виртуальные функции.
QSqlDriverCreator — шаблонный класс, предоставляющий фабрику SQL драйвера для специфичного типа драйвера. Шаблонный параметр должен быть подклассом QSqlDriver.
QSqlCreatorBase — базовый класс для фабрик SQL драйверов, чтобы возвращать экземпляр специфичного подкласса класса QSqlDriver.
QSqlDatabase несет ответственность за загрузку и управление плагинов драйверов баз данных. Когда база данных добавлена с помощью функции QSqlDatabase::addDatabase()), необходимый плагин драйвера загружается, используя QSqlDriverPlugin. QSqlDriverPlugin предоставляет собой абстрактный базовый класс для пользовательских QSqlDriver плагинов.
QSqlResult предоставляет абстрактный интерфейс для доступа к данным специфичных БД. В курсовом проекте мы используем QSqlQuery вместо QSqlResult, поскольку QSqlQuery предоставляет обертку для БД-специфичных реализации QSqlResult.
В проекте файла ipk2012.pro записываются все основные подключения и название проекта. Для того чтобы, проект мог работать с базой банных и обрабатывать запросы на sql в первой строке указывается код: sql: QT += core gui sql.
В файле mainwindow.h указывается подключение нужных библиотек, описание выбранного класса, описание конструктора, описание записанных слотов (записываются автоматически при создании), описание соответствующих указателей и функций (листинг 1.1).
Листинг 1.1 – Подключение библиотек в QT
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow> окно Qmainwindow
#include <QTextCodec> кодеки для использования русских симовлов
#include <QtSql/QSqlDatabase> создание или подключение базы
#include <QtSql/QSqlRelationalTableModel> создание реляционной таблицы модели
#include <QApplication> создание аппликации
#include <QDataWidgetMapper> создание определённых виджетов
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow описание класса и наследование
{
Q_OBJECT
public:
explicit MainWindow(QApplication *a, QWidget *parent = 0); описание конструктора
~MainWindow(); описание деструктора
private slots: описание слотов
void on_actionExit_triggered();
void on_action_triggered();
void on_action_2_triggered();
void on_action_3_triggered();
void on_action_4_triggered();
void on_action_5_triggered();
void on_action_6_triggered();
void on_action_7_triggered();
void on_action_8_triggered();
void on_action_9_triggered();
void on_action_10_triggered();
void on_action_11_triggered();
void on_action_12_triggered();
void on_action_13_triggered();
void on_action_14_triggered();
void on_action_15_triggered();
private:
Ui::MainWindow *ui; указатель на класс
QTextCodec *codec; указатель на кодеки
QSqlDatabase *db; указатель на подключение или создание
QAbstractTableModel *model; указатель на абстрактную модель таблицы
bool createConnection(); функция создания подключения
void createRelationalTables(); функция создания реляционной таблицы
void initializeModel(); функция инициализации модели
};
#endif // MAINWINDOW_H
ПВ файле mainwindow.cpp записывается подключения нужных библиотек, файлов, а также весь процесс реализации данного класса mainwindow (листинг 3.2).
Листинг 1.2 – Реализация класса mainwindow
#include "mainwindow.h"
#include "ui_mainwindow.h" подключения файла данной формы
#include <QtSql/QSqlDatabase> создания или подключение базы
#include <QtSql/QSqlError> ошибок подключения
#include <QtSql/QSqlQuery> ошибок запросов
#include <QMessageBox> текстовых сообщений
#include <QtSql/QSqlTableModel> создания реляционной таблицы
#include <QDataWidgetMapper> создания определённых виджетов
#include <QFile> создания для работы с файлами
#include <QSqlRelationalDelegate> создания реляционного делегата типа sql
#include <QDate> создания функции для работы с датами в Qt4
#include <QApplication> создания аппликации
#include <QPushButton.h> создания виджета типа QPushButton
#include <QPrinter> создания класса Qprinter
#include <QTextDocument> создания класса QTextDocument
#include <QPrintDialog> создания класса QPrintDialog
#include <QTextCodec> создания кодеков для отображения текста
#include "dialog.h" подключение файлов формы диалог
#include "dialog2.h"
MainWindow::MainWindow(QApplication *a, QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow) процесс реализации класса и конструктора с параметрами и выделения памяти
{
ui->setupUi(this); указатель на setupUi
QTextCodec *codec = QTextCodec::codecForName("CP1251");
codec = QTextCodec::codecForName("CP1251"); установка кодировки сp1251 для отображения русских символов среде Qt
QTextCodec::setCodecForTr(codec);
QTextCodec::setCodecForCStrings(codec);
QTextCodec::setCodecForLocale(codec);
ui->tw->horizontalHeader()->setResizeMode(QHeaderView::Stretch); установка горизонтальной шапки таблицы
ui->tw->setItemDelegate(new QSqlRelationalDelegate(ui->tw)); создание реляционного делегата типа sql и выделения для него памяти
ui->tw->setWindowTitle("Информационная система торговой организации");
QSqlDatabase db=QSqlDatabase::addDatabase("QMYSQL"); подключение к MYSQL
db.setHostName("127.0.0.1"); Имя хоста
db.setPort(3306); Порт
db.setDatabaseName("ipk2012"); Название базы данных MYSQL
db.setUserName("root"); Имя пользователя базы данных MYSQL
db.setPassword("12345"); Пароль
if (!db.open())
{
QMessageBox::critical(parent,QObject::tr("Database Error"),db.lastError().text());
} Процесс вывода ошибки при невозможности подключиться к базе данных
QSqlTableModel *model=new QSqlTableModel; создание реляционной модели и выделение для неё памяти
model->setTable("trade");
if (model->lastError().isValid() ) процедура вывода ошибки
{
QMessageBox::critical(parent,QObject::tr("Query Error"),model->lastError().text());
}
model->setHeaderData(0, Qt::Horizontal, QObject::tr("Номер точки"));
model->setHeaderData(1, Qt::Horizontal, QObject::tr("Тип торговой точки"));
model->setHeaderData(2, Qt::Horizontal, QObject::tr("Число торговых залов"));
model->setHeaderData(3, Qt::Horizontal, QObject::tr("Платежи за аренду, руб."));
model->setHeaderData(4, Qt::Horizontal, QObject::tr("Коммунальные услуги, руб.")); Описание шапки таблицы с помощью функции setHeaderData
model->select(); Функция выборки
ui->tw->setModel(model); Указатель на модель
this->model = model;
ui->tw->show(); Функция show-вывод на экран
model->submitAll(); Функция submitall-представляет запись всех изменений
}
MainWindow::~MainWindow() Деструктор класса
{
delete ui;
}
Процедура выполнения готовых всех классов выполняется в файле main.cpp (листинг 1.3).
Листинг 1.3 – Выполнение классов в файле main.cpp
#include <QtGui/QApplication> подключение библиотеки QApplication
#include "mainwindow.h" подключение файла mainwindow.h
#include <QTextCodec> подключение библиотеки QTextCodec
int main(int argc, char *argv[])
{
QApplication a(argc, argv); выполнение класса QApplication a
MainWindow w(&a); выполнение класса MainWindow w(&a)
w.show(); вывод на экран класс w.show();
return a.exec();
}
Для вывода таблиц используется виджет Qtableview (модель таблицы для отображения таблиц базы данных и запросов). Описывая слот и процедуру, применяется обработка события мыши типа щелчок. Все данные процедуры события автоматически записываются в файл mainwindow.сpp. Здесь указывается алгоритм вывода таблицы или определенного запроса (листинг 1.4).
Листинг 1.4 – Вывод таблиц с помощью функции QSqlTableModel
void MainWindow::on_actionExit_triggered()
{
QMessageBox::information(0,"Info","exit pressed.");
}
void MainWindow::on_action_triggered()
{
QSqlTableModel *model=new QSqlTableModel;
model->setTable("trade");
model->setHeaderData(0, Qt::Horizontal, QObject::tr("Номер точки"));
model->setHeaderData(1, Qt::Horizontal, QObject::tr("Тип торговой точки"));
model->setHeaderData(2, Qt::Horizontal, QObject::tr("Число торговых залов"));
model->setHeaderData(3, Qt::Horizontal, QObject::tr("Платежи за аренду, руб."));
model->setHeaderData(4, Qt::Horizontal, QObject::tr("Коммунальные услуги, руб."));
model->select();
ui->tw->setModel(model);
delete this->model;
this->model = model;
ui->tw->show();
}
Текст обработки события для добавления строки имеет такой код (листинг 1.5):
Листинг 1.5 – Добавление строки
void MainWindow::on_action_8_triggered()
{
model->insertRow(model->rowCount());
}
При удалении строки необходимо ввести параметр currentIndex для того, чтобы происходил процесс удаления текущей строки (удаление строки):
Листинг 3.6 – Удаление строки
void MainWindow::on_action_9_triggered()
{
model->removeRow(ui->tw->currentIndex().row());
}
Для выполнения однотабличных и многотабличных запросов в клиентском приложении использовался простой вывод таблицы в окне mainwindow по выбранным запросам и диалоговые окна с функцией выбора параметра.
Для реализации диалогового окна в визуальном файле dialog.ui указываются нужные виджеты: label (текст описания поля) и lineedit (поле для введения данных пользователем).
Листинг 1.7 – Описание файла dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog> подключение библиотеки Qdialog
namespace Ui {
class Dialog;
}
class Dialog : public QDialog описание класса и наследование Qdialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0); конструктор типа dialog
~Dialog(); деструктор типа dialog
QString search();QString search_2();QString search_3();QString search_4(); описание функций типа qstring для записи текста из виджета типа lineedit
private slots:
private:
Ui::Dialog *ui; указатель ui в форме dialog
};
#endif // DIALOG_H
В файле dialog.cpp подключается заголовочный файл dialog.h и ui_dialog.h. (листинг 3.8). Здесь происходит процесс реализации класса и конструктора dialog, выделение записи для конструктора и реализация функции QString search().
Листинг 1.8 – Реализации класса, конструктора dialog и функции QString search()
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
}
Dialog::~Dialog()
{
delete ui;
}
QString Dialog::search()
{
return ui->lineEdit->text();
}
QString Dialog::search_2()
{
return ui->lineEdit_2->text();
}
В ходе разработки приложения были разработаны следующие типы запросов:
1. Поиск продавцов по критерию «Фамилия» и номеру торговой точки (листинг 1.9)
Листинг 1.9 – Тип запроса «Поиск продавцов по критерию «Фамилия» и номеру торговой точки»
void MainWindow::on_action_10_triggered()
{
Dialog dialog;
if (dialog.exec())
{
QString p=dialog.search();QString d=dialog.search_2();
QSqlError sqlError;
QSqlQueryModel *model = new QSqlQueryModel;
if (d=="") { model->setQuery("select seller_id, seller_family, seller_name, seller_oklad, trade_id from seller WHERE seller_family = \'"+p+"\' ");
model->setHeaderData(0, Qt::Horizontal, QObject::tr("Номер"));
model->setHeaderData(1, Qt::Horizontal, QObject::tr("Фамилия продавца"));
model->setHeaderData(2, Qt::Horizontal, QObject::tr("Имя продавца"));
model->setHeaderData(3, Qt::Horizontal, QObject::tr("Оклад"));
model->setHeaderData(4, Qt::Horizontal, QObject::tr("Номер торговой точки"));
}
if(d!="") {
model->setQuery("select seller_id, seller_family, seller_name, seller_oklad, trade_id from seller WHERE seller_family = \'"+p+"\' and trade_id =\'"+d+"\' ");
model->setHeaderData(0, Qt::Horizontal, QObject::tr("Номер"));
model->setHeaderData(1, Qt::Horizontal, QObject::tr("Фамилия продавца"));
model->setHeaderData(2, Qt::Horizontal, QObject::tr("Имя продавца"));
model->setHeaderData(3, Qt::Horizontal, QObject::tr("Оклад"));
model->setHeaderData(4, Qt::Horizontal, QObject::tr("Номер торговой точки"));
}
sqlError = model->lastError();
if (sqlError.type()!=QSqlError::NoError)
{
QMessageBox::critical(this,"SQL Error", sqlError.text());
}
//! [3]
ui->tw->setModel(model);
delete this->model;
this->model = model;
}
}
2. Общее количество товаров на складе (листинг 1.10)
Листинг 1.10 – Тип запроса «Количество товаров на складе»
void MainWindow::on_action_11_triggered()
{
QSqlQueryModel *model=new QSqlQueryModel;
model->setQuery("SELECT catalog_id, catalog_name, catalog_sum FROM catalog");
model->setHeaderData(0, Qt::Horizontal, QObject::tr("Номер товара"));
model->setHeaderData(1, Qt::Horizontal, QObject::tr("Название товара"));
model->setHeaderData(2, Qt::Horizontal, QObject::tr("Количество"));
ui->tw->setModel(model);
delete this->model;
this->model = model;
ui->tw->show();
}
3. Количество оформленных покупателями товаров с сортировкой по полю дата (листинг 1.11)
Листинг 1.11 – Тип запроса «Оформленные заказы с сортировкой по дате»
void MainWindow::on_action_12_triggered()
{
QSqlQueryModel *model=new QSqlQueryModel;
model->setQuery("SELECT catalog_id, sales_sum, sales_price, sales_date from sales ORDER BY sales_date DESC");
model->setHeaderData(0, Qt::Horizontal, QObject::tr("Номер товара"));
model->setHeaderData(1, Qt::Horizontal, QObject::tr("Количество товаров"));
model->setHeaderData(2, Qt::Horizontal, QObject::tr("Стоимость товара"));
model->setHeaderData(3, Qt::Horizontal, QObject::tr("Дата оформления заказа"));
ui->tw->setModel(model);
delete this->model;
this->model = model;
ui->tw->show();
}
4. Оформленные товары с фамилией и именем покупателей с сортировкой по дате (многотабличный запрос) (листинг 1.12)
Листинг 1.12 – Тип запроса «Оформленные товары покупателей с сортировкой по дате»
void MainWindow::on_action_13_triggered() // многотабличный запрос
{
QSqlQueryModel *model=new QSqlQueryModel;
model->setQuery("SELECT buyer.buyer_name, buyer.buyer_family, catalog.catalog_name, sales.sales_date from buyer, catalog, sales WHERE buyer.buyer_id = catalog.catalog_id AND catalog.catalog_id = sales.sales_id ORDER BY sales_date DESC");
model->setHeaderData(0, Qt::Horizontal, QObject::tr("Имя"));
model->setHeaderData(1, Qt::Horizontal, QObject::tr("Фамилия"));
model->setHeaderData(2, Qt::Horizontal, QObject::tr("Название товара"));
model->setHeaderData(3, Qt::Horizontal, QObject::tr("Дата оформления заказа"));
ui->tw->setModel(model);
delete this->model;
this->model = model;
ui->tw->show();
}
5. Сумма и количество проданных товаров в торговых точках (многотабличный запрос с реализацией диалогового окна) (листинг 1.13)
Листинг 1.13 – Тип запроса «Сумма и количество проданных товаров»
void MainWindow::on_action_14_triggered()
{
Dialog2 dialog2;
if (dialog2.exec())
{
QString p=dialog2.search();QString d=dialog2.search_2();
QSqlError sqlError;
QSqlQueryModel *model = new QSqlQueryModel;
if ( d=="" ) {model->setQuery("select trade.trade_id, trade.trade_type, seller.seller_family, sum(sales.sales_price) as saleprice01, sum(sales.sales_sum) as saleprice02 from trade, seller, sales where trade.trade_id=\'"+p+"\' and trade.trade_id=seller.seller_id and seller.seller_id=sales.sales_id;") ;
model->setHeaderData(0, Qt::Horizontal, QObject::tr("Номер торговой точки"));
model->setHeaderData(1, Qt::Horizontal, QObject::tr("Название торговой точки"));
model->setHeaderData(2, Qt::Horizontal, QObject::tr("Продавец точки "));
model->setHeaderData(3, Qt::Horizontal, QObject::tr("Общая сумма проданных товаров"));
model->setHeaderData(4, Qt::Horizontal, QObject::tr("Количество проданных товаров"));}
if(d!=""){ model->setQuery("select trade.trade_id, trade.trade_type, seller.seller_family, sum(sales.sales_price) as saleprice01, sum(trade.trade_rent) as saleprice03 from trade, seller, sales where trade.trade_id=\'"+p+"\' and trade.trade_type=\'"+d+"\' and trade.trade_id=seller.seller_id and seller.seller_id=sales.sales_id;") ;
model->setHeaderData(0, Qt::Horizontal, QObject::tr("Номер торговой точки"));
model->setHeaderData(1, Qt::Horizontal, QObject::tr("Название торговой точки"));
model->setHeaderData(2, Qt::Horizontal, QObject::tr("Продавец точки "));
model->setHeaderData(3, Qt::Horizontal, QObject::tr("Общая сумма проданных товаров"));
model->setHeaderData(4, Qt::Horizontal, QObject::tr("Оплата за аренду"));}
sqlError = model->lastError();
if (sqlError.type()!=QSqlError::NoError) {
QMessageBox::critical(this,"SQL Error", sqlError.text());
}
//! [3]
ui->tw->setModel(model);
delete this->model;
this->model = model;
}
}
6. Дополнительный запрос с функцией вывода на печать с сохранением текста в формате .pdf (листинг 1.14)
Листинг 1.14 – Тип запроса «Вывод на печать»
void MainWindow::on_action_41_triggered()
{
ui->tw->setModel(model); указатель модель, использованную в данный момент
QPrinter printer; создание класса типа Qprinter
QTextDocument doc; создание класса типа QTextDocument
QString html; создание переменной типа QString
QPrintDialog printDialog(&printer,this); создание класса QPrintDialog с параметрами класса QPrinter , с помощью которого происходит вывод на печать
if (printDialog.exec()) {
html+="<table bordercolor='green' border='2' width='100%'cols='10'>";
html += "<tr>"; установка горизонтальной шапки таблицы
for (int l=0,p=model->columnCount();l<p;++l) {
html += "<td>" + model->headerData(l,Qt::Horizontal,0).toString() + "</td>";
}
html += "</tr>";
for (int i=0,n=model->rowCount();i<n;++i) {
html += "<tr>";
for (int j=0,m=model->columnCount();j<m;++j) {
html += "<td>" + model->data(model->index(i,j)).toString() + "</td>";
}
html += "</tr>";
}
html+="</table>"; закрывающийся тег таблицы
doc.setHtml(html); выполнение функции переноса нашей таблицы в текстовый документ .doc
printer.setOutputFormat(QPrinter::PdfFormat); перевод в формат .pdf
printer.setOutputFileName("example.pdf"); сохранение текста в формате pdf
doc.print(&printer); выполнение печати
}
}
Таким образом, основным преимуществом Qt является возможность запускать написанное приложение на большинстве современных операционных систем путём простой компиляции программы для каждой ОС без изменения исходного кода. Qt является полностью объектно-ориентированным, легко расширяемым и поддерживающим технику компонентного программирования.
Все исходные файлы проекта, курсовая работа и презентация клиентского приложения находится в прикрепленных файлах.