From d2f391558a02ca2a4a6ea29230d5df043512f37e Mon Sep 17 00:00:00 2001 From: Josef Rokos Date: Tue, 29 Dec 2015 16:09:55 +0100 Subject: [PATCH] UI for data filtering. Icons for plugin grid operations. --- accommodation/accommodation.cpp | 3 +- accommodation/accommodation.h | 2 +- accommodation/accommodation.pro | 3 +- application/application.pro | 2 + application/mainwindow.ui | 8 +- core/autotablemodel.h | 32 +++++ core/core.pro | 16 ++- core/exprevaluator.cpp | 41 +++++++ core/exprevaluator.h | 26 ++++ core/filterui.cpp | 208 ++++++++++++++++++++++++++++++++ core/filterui.h | 43 +++++++ core/filterui.ui | 160 ++++++++++++++++++++++++ core/gridform.h | 4 + core/gridform.ui | 89 +++++++++++++- core/icons/delete.svg | 1 + core/icons/edit.svg | 13 ++ core/icons/filter.svg | 6 + core/icons/new.svg | 6 + core/icons/print.svg | 10 ++ core/icons/remove.svg | 6 + core/igridform.cpp | 20 +++ core/igridform.h | 8 +- core/rc.qrc | 6 + 23 files changed, 700 insertions(+), 13 deletions(-) create mode 100644 core/exprevaluator.cpp create mode 100644 core/exprevaluator.h create mode 100644 core/filterui.cpp create mode 100644 core/filterui.h create mode 100644 core/filterui.ui create mode 100644 core/icons/delete.svg create mode 100644 core/icons/edit.svg create mode 100644 core/icons/filter.svg create mode 100644 core/icons/new.svg create mode 100644 core/icons/print.svg create mode 100644 core/icons/remove.svg diff --git a/accommodation/accommodation.cpp b/accommodation/accommodation.cpp index f8d8795..f01ad27 100644 --- a/accommodation/accommodation.cpp +++ b/accommodation/accommodation.cpp @@ -23,7 +23,7 @@ void Accommodation::initServiceUi() m_service = service; m_ui = grid; } - +/* QWidget *Accommodation::ui() { QWidget *ui = IPlugin::ui(); @@ -33,3 +33,4 @@ QWidget *Accommodation::ui() return ui; } +*/ diff --git a/accommodation/accommodation.h b/accommodation/accommodation.h index 9dbf7ff..7416dda 100644 --- a/accommodation/accommodation.h +++ b/accommodation/accommodation.h @@ -26,7 +26,7 @@ protected: // IPlugin interface public: - QWidget *ui(); + //QWidget *ui(); }; #endif // ACCOMMODATION_H diff --git a/accommodation/accommodation.pro b/accommodation/accommodation.pro index f90f628..edfc4da 100644 --- a/accommodation/accommodation.pro +++ b/accommodation/accommodation.pro @@ -9,7 +9,8 @@ QT += widgets sql TARGET = accommodation TEMPLATE = lib -DEFINES += ACCOMMODATION_LIBRARY +DEFINES += ACCOMMODATION_LIBRARY\ + _GLIBCXX_USE_CXX11_ABI=0 SOURCES += accommodation.cpp \ data/person.cpp \ diff --git a/application/application.pro b/application/application.pro index 5333ecb..2205f9a 100644 --- a/application/application.pro +++ b/application/application.pro @@ -11,6 +11,8 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = prodejna TEMPLATE = app +DEFINES += _GLIBCXX_USE_CXX11_ABI=0 + win32 { INCLUDEPATH += d:/prac/odb/libodb-2.4.0 INCLUDEPATH += d:/prac/odb/libodb-qt-2.4.0 diff --git a/application/mainwindow.ui b/application/mainwindow.ui index 644d849..d408b6b 100644 --- a/application/mainwindow.ui +++ b/application/mainwindow.ui @@ -6,8 +6,8 @@ 0 0 - 825 - 538 + 1000 + 700 @@ -43,8 +43,8 @@ 0 0 - 825 - 21 + 1000 + 19 diff --git a/core/autotablemodel.h b/core/autotablemodel.h index af977d5..b00c404 100644 --- a/core/autotablemodel.h +++ b/core/autotablemodel.h @@ -8,6 +8,7 @@ #include "define.h" #include "core_global.h" +#include "exprevaluator.h" template class AutoTableModel : public QAbstractTableModel @@ -17,6 +18,7 @@ public: explicit AutoTableModel(QObject *parent = NULL) :QAbstractTableModel(parent) { + filtered = false; } virtual ~AutoTableModel() {} @@ -139,8 +141,38 @@ public: m_list = list; } +public slots: + void filter(const QString &filter) + { + beginResetModel(); + if (!filtered) + { + m_fullList = m_list; + filtered = true; + } + + ExprEvaluator ev; + m_list.clear(); + std::copy_if(ALL(m_fullList), m_list.begin(), [&filter, &ev](QSharedPointer obj){ return ev.evaluate((QObject*)obj.pointer, filter); }); + + endResetModel(); + } + + void restore() + { + if (filtered) + { + beginResetModel(); + m_list = m_fullList; + endResetModel(); + filtered = false; + } + } + private: QList > m_list; + QList > m_fullList; + bool filtered; }; diff --git a/core/core.pro b/core/core.pro index 6369b4e..9fe8998 100644 --- a/core/core.pro +++ b/core/core.pro @@ -4,12 +4,15 @@ # #------------------------------------------------- +#iconset: https://www.iconfinder.com/iconsets/snipicons + QT += widgets sql TARGET = core TEMPLATE = lib -DEFINES += CORE_LIBRARY +DEFINES += CORE_LIBRARY \ + _GLIBCXX_USE_CXX11_ABI=0 SOURCES += \ data/user.cpp \ @@ -33,7 +36,9 @@ SOURCES += \ roles/roles.cpp \ roles/rolesui.cpp \ roles/rolesform.cpp \ - permissionservice.cpp + permissionservice.cpp \ + filterui.cpp \ + exprevaluator.cpp HEADERS += core.h\ core_global.h \ @@ -66,7 +71,9 @@ HEADERS += core.h\ roles/roles.h \ roles/rolesui.h \ roles/rolesform.h \ - permissionservice.h + permissionservice.h \ + filterui.h \ + exprevaluator.h unix { target.path = /usr/lib @@ -95,7 +102,8 @@ FORMS += \ formdialog.ui \ users/userform.ui \ columndialog.ui \ - roles/rolesform.ui + roles/rolesform.ui \ + filterui.ui OTHER_FILES += \ users/metaData.json \ diff --git a/core/exprevaluator.cpp b/core/exprevaluator.cpp new file mode 100644 index 0000000..3bfb131 --- /dev/null +++ b/core/exprevaluator.cpp @@ -0,0 +1,41 @@ +#include "exprevaluator.h" + +#ifdef _MSC_VER +ExprEvaluator::ExprEvaluator() +{ + m_operations["=="] = [](QVariant left, QVariant right) { return left == right; }; + m_operations["!="] = [](QVariant left, QVariant right) { return left != right; }; + m_operations["<"] = [](QVariant left, QVariant right) { return left < right; }; + m_operations["<="] = [](QVariant left, QVariant right) { return left <= right; }; + m_operations[">"] = [](QVariant left, QVariant right) { return left > right; }; + m_operations[">="] = [](QVariant left, QVariant right) { return left >= right; }; + m_operations["%"] = [](QVariant left, QVariant right) { return left.toString().contains(right.toString()); }; + + m_operations["||"] = [](QVariant left, QVariant right) { return left.toBool() || right.toBool(); }; + m_operations["&&"] = [](QVariant left, QVariant right) { return left.toBool() && right.toBool(); }; +} +#else +const QMap > ExprEvaluator::m_operations = { + { "==", [](QVariant left, QVariant right) { return left == right; }}, + { "!=", [](QVariant left, QVariant right) { return left != right; }}, + { "<", [](QVariant left, QVariant right) { return left < right; }}, + { "<=", [](QVariant left, QVariant right) { return left <= right; }}, + { ">", [](QVariant left, QVariant right) { return left > right; }}, + { ">=", [](QVariant left, QVariant right) { return left >= right; }}, + { "%", [](QVariant left, QVariant right) { return left.toString().contains(right.toString()); }}, + + { "||", [](QVariant left, QVariant right) { return left.toBool() || right.toBool(); }}, + { "&&", [](QVariant left, QVariant right) { return left.toBool() && right.toBool(); }} +}; + +ExprEvaluator::ExprEvaluator() +{ +} +#endif + + +bool ExprEvaluator::evaluate(const QObject *data, const QString &expression) +{ + return true; +} + diff --git a/core/exprevaluator.h b/core/exprevaluator.h new file mode 100644 index 0000000..61fcc62 --- /dev/null +++ b/core/exprevaluator.h @@ -0,0 +1,26 @@ +#ifndef EXPREVALUATOR_H +#define EXPREVALUATOR_H + +#include +#include +#include +#include + +class ExprEvaluator +{ +public: + ExprEvaluator(); + + bool evaluate(const QObject *data, const QString &expression); + +private: +#ifdef _MSC_VER + QMap > m_operations; +#else + static const QMap > m_operations; +#endif + + bool m_caseSensitive; +}; + +#endif // EXPREVALUATOR_H diff --git a/core/filterui.cpp b/core/filterui.cpp new file mode 100644 index 0000000..01cbac7 --- /dev/null +++ b/core/filterui.cpp @@ -0,0 +1,208 @@ +#include "filterui.h" +#include "ui_filterui.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +FilterUi::FilterUi(QWidget *parent, QObject *entity) : + QWidget(parent), + ui(new Ui::FilterUi) +{ + ui->setupUi(this); + m_entity = entity; + + if (entity == NULL) + { + return; + } + + m_entity->setParent(this); + addRow(false); + + m_contextMenu = new QMenu(this); + m_contextMenu->addAction(ui->actionAdd_condition_AND); + m_contextMenu->addAction(ui->actionAdd_condition_OR); +} + +FilterUi::~FilterUi() +{ + delete ui; +} + +void FilterUi::on_actionAdd_condition_AND_triggered() +{ + addRow(false); +} + +void FilterUi::on_actionAdd_condition_OR_triggered() +{ + addRow(true); +} + +void FilterUi::addRow(bool isOr) +{ + if (m_entity == NULL) + { + Q_ASSERT(false); + return; + } + + int row = ui->tableWidget->rowCount(); + ui->tableWidget->setRowCount(row + 1); + QComboBox *properties = new QComboBox(this); + + for (int i = 1; i < m_entity->metaObject()->propertyCount(); i++) + { + properties->addItem(tr(m_entity->metaObject()->property(i).name()), m_entity->metaObject()->property(i).name()); + } + + QComboBox *oper = new QComboBox(this); + oper->addItems(QStringList() << "==" << "%" << "!=" << "<" << "<=" << ">" << ">="); + ui->tableWidget->setCellWidget(row, 2, oper); + + void (QComboBox::*indexChanged)(int) = &QComboBox::currentIndexChanged; + connect(properties, indexChanged, [this, oper, row](int index){ + this->propertyChanged(row, oper, index); + }); + + ui->tableWidget->setCellWidget(row, 1, properties); + + if (row > 0) + { + QComboBox *rowOper = new QComboBox(this); + rowOper->addItems(QStringList() << tr("OR") << tr("AND")); + rowOper->setCurrentIndex(isOr ? 0 : 1); + ui->tableWidget->setCellWidget(row, 0, rowOper); + + QToolButton *tb = new QToolButton(this); + tb->setText("-"); + connect(tb, &QToolButton::clicked, [this, tb](){ + int row = ui->tableWidget->rowAt(tb->y()); + ui->tableWidget->removeRow(row); + }); + ui->tableWidget->setCellWidget(row, 4, tb); + } + else + { + ui->tableWidget->setColumnWidth(1, 150); + ui->tableWidget->setColumnWidth(2, 50); + ui->tableWidget->setColumnWidth(3, 300); + ui->tableWidget->setColumnWidth(4, 50); + } + + propertyChanged(row, oper, 0); +} + +QString FilterUi::buildExpression() const +{ + QString expr; + + for (int i = 0; i < ui->tableWidget->rowCount(); i++) + { + if (i == 0) + { + expr = qobject_cast(ui->tableWidget->cellWidget(i, 1))->currentData(Qt::UserRole).toString(); + } + else + { + expr += " " + QString(qobject_cast(ui->tableWidget->cellWidget(i, 0))->currentIndex() == 0 ? "||" : "&&"); + expr += " " + qobject_cast(ui->tableWidget->cellWidget(i, 1))->currentData(Qt::UserRole).toString(); + } + + expr += " " + qobject_cast(ui->tableWidget->cellWidget(i, 2))->currentText(); + + QWidget *cellWidget = ui->tableWidget->cellWidget(i, 3); + + if (cellWidget != NULL) + { + expr += " " + cellWidget->property(cellWidget->metaObject()->userProperty().name()).toString(); + } + } + + return expr; +} + + +void FilterUi::on_tableWidget_customContextMenuRequested(const QPoint &pos) +{ + m_contextMenu->popup(ui->tableWidget->viewport()->mapToGlobal(pos)); +} + +void FilterUi::on_actionRemove_condition_triggered() +{ + QObject *action = sender(); + ui->tableWidget->removeRow(action->property("row").toInt()); +} + +void FilterUi::on_btnGo_toggled(bool checked) +{ + if (checked) + { + emit applyFilter(buildExpression()); + } + else + { + emit restoreData(); + } +} + +void FilterUi::propertyChanged(int row, QComboBox *oper, int index) +{ + QWidget *cellWidget = ui->tableWidget->cellWidget(row, 3); + ui->tableWidget->removeCellWidget(row, 3); + + if (cellWidget != NULL) + { + delete cellWidget; + cellWidget = NULL; + } + + oper->clear(); + + switch (this->m_entity->metaObject()->property(index + 1).type()) { + case QVariant::Bool: + oper->addItems(QStringList() << "==" << "!="); + cellWidget = new QComboBox(this); + qobject_cast(cellWidget)->addItem("true", 1); + qobject_cast(cellWidget)->addItem("false", 0); + break; + case QVariant::String: + oper->addItems(QStringList() << "==" << "%" << "!=" << "<" << "<=" << ">" << ">="); + cellWidget = new QLineEdit(this); + break; + case QVariant::Int: + oper->addItems(QStringList() << "==" << "!=" << "<" << "<=" << ">" << ">="); + cellWidget = new QSpinBox(this); + break; + case QVariant::Double: + oper->addItems(QStringList() << "==" << "!=" << "<" << "<=" << ">" << ">="); + cellWidget = new QDoubleSpinBox(this); + break; + case QVariant::Date: + oper->addItems(QStringList() << "==" << "!=" << "<" << "<=" << ">" << ">="); + cellWidget = new QDateEdit(this); + qobject_cast(cellWidget)->setCalendarPopup(true); + break; + case QVariant::DateTime: + oper->addItems(QStringList() << "==" << "!=" << "<" << "<=" << ">" << ">="); + cellWidget = new QDateTimeEdit(this); + qobject_cast(cellWidget)->setCalendarPopup(true); + break; + default: + oper->addItems(QStringList() << "==" << "!=" << "<" << "<=" << ">" << ">="); + cellWidget = new QLineEdit(this); + break; + } + + if (cellWidget != NULL) + { + ui->tableWidget->setCellWidget(row, 3, cellWidget); + } +} diff --git a/core/filterui.h b/core/filterui.h new file mode 100644 index 0000000..7095a32 --- /dev/null +++ b/core/filterui.h @@ -0,0 +1,43 @@ +#ifndef FILTERUI_H +#define FILTERUI_H + +#include +#include +#include + +#include "core_global.h" + +namespace Ui { +class FilterUi; +} + +class CORESHARED_EXPORT FilterUi : public QWidget +{ + Q_OBJECT + +public: + explicit FilterUi(QWidget *parent = 0, QObject *entity = NULL); + ~FilterUi(); + +private slots: + void on_actionAdd_condition_AND_triggered(); + void on_actionAdd_condition_OR_triggered(); + void on_tableWidget_customContextMenuRequested(const QPoint &pos); + void on_actionRemove_condition_triggered(); + void on_btnGo_toggled(bool checked); + +signals: + void applyFilter(const QString &filter); + void restoreData(); + +private: + Ui::FilterUi *ui; + void addRow(bool isOr); + QObject *m_entity; + QMenu *m_contextMenu; + + QString buildExpression() const; + void propertyChanged(int row, QComboBox *oper, int index); +}; + +#endif // FILTERUI_H diff --git a/core/filterui.ui b/core/filterui.ui new file mode 100644 index 0000000..8758a45 --- /dev/null +++ b/core/filterui.ui @@ -0,0 +1,160 @@ + + + FilterUi + + + + 0 + 0 + 667 + 177 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 16777215 + 150 + + + + + 6 + + + 0 + + + 0 + + + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Load filter: + + + + + + + + + + Go + + + true + + + + + + + Save + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::CustomContextMenu + + + QAbstractItemView::NoEditTriggers + + + 0 + + + 5 + + + false + + + false + + + + + + + + + + + + + + + Add condition "AND" + + + + + Add condition "OR" + + + + + Remove condition + + + + + + diff --git a/core/gridform.h b/core/gridform.h index ab3bf4d..cc8cb6b 100644 --- a/core/gridform.h +++ b/core/gridform.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "autoform.h" #include "autotablemodel.h" @@ -23,6 +24,9 @@ public: m_form = NULL; m_tableModel = NULL; m_formHandler = new DefaultFormHandler(); + + m_filterUi = new FilterUi(this, new T); + filterWidget()->layout()->addWidget(m_filterUi); } virtual ~GridForm() { diff --git a/core/gridform.ui b/core/gridform.ui index e09120b..adb0a8f 100644 --- a/core/gridform.ui +++ b/core/gridform.ui @@ -19,9 +19,28 @@ + + false + N + + + :/icons/new.svg:/icons/new.svg + + + + 24 + 24 + + + + Qt::ToolButtonFollowStyle + + + true + @@ -29,6 +48,19 @@ E + + + :/icons/edit.svg:/icons/edit.svg + + + + 24 + 24 + + + + true + @@ -36,6 +68,19 @@ D + + + :/icons/remove.svg:/icons/remove.svg + + + + 24 + 24 + + + + true + @@ -43,6 +88,22 @@ F + + + :/icons/filter.svg:/icons/filter.svg + + + + 24 + 24 + + + + true + + + true + @@ -50,6 +111,19 @@ P + + + :/icons/print.svg:/icons/print.svg + + + + 24 + 24 + + + + true + @@ -68,6 +142,17 @@ + + + + + 16777215 + 150 + + + + + @@ -91,6 +176,8 @@ - + + + diff --git a/core/icons/delete.svg b/core/icons/delete.svg new file mode 100644 index 0000000..2833017 --- /dev/null +++ b/core/icons/delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/core/icons/edit.svg b/core/icons/edit.svg new file mode 100644 index 0000000..14734eb --- /dev/null +++ b/core/icons/edit.svg @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/core/icons/filter.svg b/core/icons/filter.svg new file mode 100644 index 0000000..6b38d75 --- /dev/null +++ b/core/icons/filter.svg @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/core/icons/new.svg b/core/icons/new.svg new file mode 100644 index 0000000..e707c9d --- /dev/null +++ b/core/icons/new.svg @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/core/icons/print.svg b/core/icons/print.svg new file mode 100644 index 0000000..aec7314 --- /dev/null +++ b/core/icons/print.svg @@ -0,0 +1,10 @@ + \ No newline at end of file diff --git a/core/icons/remove.svg b/core/icons/remove.svg new file mode 100644 index 0000000..83c43c2 --- /dev/null +++ b/core/icons/remove.svg @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/core/igridform.cpp b/core/igridform.cpp index 3f2634a..b784401 100644 --- a/core/igridform.cpp +++ b/core/igridform.cpp @@ -4,12 +4,14 @@ #include #include "context.h" +#include "filterui.h" IGridForm::IGridForm(QWidget *parent) : QWidget(parent), ui(new Ui::GridForm) { ui->setupUi(this); + ui->filterWidget->setVisible(false); m_contextMenu = new QMenu(this); m_contextMenu->addAction(ui->actionSelectColumns); @@ -43,6 +45,11 @@ void IGridForm::hideColumns(const QList &cols) } } +QWidget *IGridForm::filterWidget() +{ + return ui->filterWidget; +} + void IGridForm::on_btnNew_clicked() { @@ -88,3 +95,16 @@ void IGridForm::columnsAccepted() Context::instance().settings()->setValue("grids/" + pluginId() + "/hide", QVariant::fromValue(varList)); } + + +void IGridForm::on_btnFilter_toggled(bool checked) +{ + if (checked) + { + ui->filterWidget->setVisible(true); + } + else + { + ui->filterWidget->setVisible(false); + } +} diff --git a/core/igridform.h b/core/igridform.h index 7c676a9..499dc6a 100644 --- a/core/igridform.h +++ b/core/igridform.h @@ -8,6 +8,7 @@ #include #include "columndialog.h" +#include "filterui.h" #include "defaultformhandler.h" #include "core_global.h" @@ -37,8 +38,8 @@ protected: virtual void handleNewRecord() = 0; virtual void handleEditRecord() = 0; virtual void handleDeleteRecord() = 0; - void hideColumns(const QList &cols); + QWidget *filterWidget(); private slots: void on_btnNew_clicked(); @@ -48,12 +49,17 @@ private slots: void on_actionSelectColumns_triggered(); void columnsAccepted(); + void on_btnFilter_toggled(bool checked); + private: QString m_pluginId; IFormHandler *m_formHandler; Ui::GridForm *ui; QMenu *m_contextMenu; ColumnDialog *m_columnDialog; + +protected: + FilterUi *m_filterUi; }; #endif // IGRIDFORM_H diff --git a/core/rc.qrc b/core/rc.qrc index 3bb3eff..ab51ecb 100644 --- a/core/rc.qrc +++ b/core/rc.qrc @@ -3,5 +3,11 @@ metaData.json users/metaData.json roles/metaData.json + icons/new.svg + icons/edit.svg + icons/delete.svg + icons/remove.svg + icons/filter.svg + icons/print.svg