Разработка клиентской части приложения информационной системы торговой организации с помощью Qt

Время чтения
11 минут
Прочитано

Разработка клиентской части приложения информационной системы торговой организации с помощью Qt

ноября 10, 2012 - 17:32
0 комментариев

В рамках учебного курса «Средства визуального программирования приложений» (ИПКиПК) мы познакомились с Qt - кросс-платформенным инструментарием разработки ПО на языке программирования C++. Этап написания курсовой работы состоял из двух частей. В первой части, на занятиях по управлению базами данных мы разработали структуру базы данных MySQL и различные запросы, согласно своему варианту курсовой работы. Во второй части, мы разработали клиентскую часть приложения на Qt с использованием среды разработки Qt Creator и средой разработки графического интерфейса Qt Designer.

Теоретическая основа разработки автоматизированной информационной системы

В теоретической части курсовой работы рассмотрены основы разработки автоматизированной информационной системы: процесс создания, проектирования и использования базы данных MYSQL, реализация различных классов при разработке приложений с  использованием библиотеки Qt.

Проектирование и разработка клиентского приложения информационной системы торговой организации

В практической части  курсовой работы реализована структура базы данных торгового предприятия и описан процесс создания клиентского приложения с использованием программы Qt.

Проектирование базы данных торговой организации начиналось с создания всех нужных таблиц в базе, всех полей входящих в каждую таблицу, взаимодействия таблиц между собой с помощью специальных отношений (один ко многим, многие ко многим) и создание в соответствии с этими параметрами первичных и вторичных ключей.

Структура базы данных информационной системы торговой организации (файл ipk2012.sql):

Таблицы MySQL в клиентском приложении Qt

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-проекта

В библиотеке 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

Реализация класса mainwindow.cpp

#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 (поле для введения данных пользователем).

Реализация диалогового окна dialog в QT

Листинг 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().

Реализация диалоговых окон в Qt Designer

Листинг 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 является полностью объектно-ориентированным, легко расширяемым и поддерживающим технику компонентного программирования.

Все исходные файлы проекта, курсовая работа и презентация клиентского приложения находится в прикрепленных файлах.