Skip to content

Commit

Permalink
Added new dialog for generic expression builder; Added search to func…
Browse files Browse the repository at this point in the history
…tion list; Right click to load sample 10-All values
  • Loading branch information
NathanW2 committed Oct 9, 2011
1 parent eb0642e commit 6e41cc4
Show file tree
Hide file tree
Showing 8 changed files with 481 additions and 242 deletions.
49 changes: 11 additions & 38 deletions src/app/qgslabelinggui.cpp
Expand Up @@ -24,7 +24,7 @@

#include "qgspallabeling.h"
#include "qgslabelengineconfigdialog.h"
#include "qgsexpressionbuilder.h"
#include "qgsexpressionbuilderdialog.h"
#include "qgsexpression.h"

#include <QColorDialog>
Expand Down Expand Up @@ -494,43 +494,16 @@ void QgsLabelingGui::showEngineConfigDialog()
void QgsLabelingGui::showExpressionDialog()
{
//TODO extract this out to a dialog.

QDialog* dlg = new QDialog();
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok
| QDialogButtonBox::Cancel);
QGridLayout* layout = new QGridLayout();
QgsExpressionBuilderWidget* builder = new QgsExpressionBuilderWidget(mLayer);
dlg->setLayout(layout);
layout->addWidget(builder);
layout->addWidget(buttonBox);
connect(buttonBox,SIGNAL( accepted() ),dlg,SLOT( accept() ) );
connect(buttonBox,SIGNAL( rejected() ),dlg,SLOT( reject() ) );
QPushButton* okButuon = buttonBox->button(QDialogButtonBox::Ok);
connect(builder, SIGNAL(expressionParsed(bool)), okButuon, SLOT(setEnabled(bool)));

// Set the current expression using the selected text in the combo box.
builder->setExpressionString(this->cboFieldName->currentText());
builder->loadFieldNames();

if ( dlg->exec() == QDialog::Accepted )
{
QString expression = builder->getExpressionString();
//Do validation here first before applying
QgsExpression exp( expression );
if ( exp.hasParserError() )
{
//expression not valid
QMessageBox::critical( 0, "Syntax error",
"Invalid expression syntax. The error message of the parser is: '" + exp.parserErrorString() + "'" );
return;
}

// Only add the expression if the user has entered some text.
if (!expression.isEmpty())
{
cboFieldName->addItem(expression);
cboFieldName->setCurrentIndex(cboFieldName->count() - 1);
}
QgsExpressionBuilderDialog dlg( mLayer, cboFieldName->currentText() , this );
if ( dlg.exec() == QDialog::Accepted )
{
QString expression = dlg.expressionBuilder()->getExpressionString();
//Only add the expression if the user has entered some text.
if (!expression.isEmpty())
{
cboFieldName->addItem(expression);
cboFieldName->setCurrentIndex(cboFieldName->count() - 1);
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/gui/CMakeLists.txt
Expand Up @@ -71,6 +71,7 @@ qgsvertexmarker.cpp
qgsludialog.cpp
qgssearchquerybuilder.cpp
qgsexpressionbuilder.cpp
qgsexpressionbuilderdialog.cpp
)

SET(QGIS_GUI_MOC_HDRS
Expand Down Expand Up @@ -127,6 +128,7 @@ qgsprojectbadlayerguihandler.h
qgslonglongvalidator.h
qgssearchquerybuilder.h
qgsexpressionbuilder.h
qgsexpressionbuilderdialog.h
)

QT4_WRAP_CPP(QGIS_GUI_MOC_SRCS ${QGIS_GUI_MOC_HDRS})
Expand Down Expand Up @@ -160,6 +162,7 @@ qgssearchquerybuilder.h
qgsattributeeditor.h
qgsfieldvalidator.h
qgsexpressionbuilder.h
qgsexpressionbuilderdialog.h

attributetable/qgsattributetablemodel.h
attributetable/qgsattributetablememorymodel.h
Expand All @@ -177,6 +180,7 @@ ${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgscredentialdialog.h
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsprojectionselectorbase.h
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsquerybuilderbase.h
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsexpressionbuilder.h
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsexpressionbuilderdialogbase.h
)

INCLUDE_DIRECTORIES(
Expand Down
104 changes: 83 additions & 21 deletions src/gui/qgsexpressionbuilder.cpp
Expand Up @@ -17,19 +17,23 @@
#include "qgslogger.h"
#include "qgsexpression.h"

QgsExpressionBuilderWidget::QgsExpressionBuilderWidget(QgsVectorLayer *layer)
: QWidget(),
mLayer( layer )
{
if (!layer) return;
#include <QMenu>

QgsExpressionBuilderWidget::QgsExpressionBuilderWidget(QWidget *parent)
: QWidget(parent)
{
setupUi(this);

mValueListWidget->hide();
mValueListLabel->hide();

mModel = new QStandardItemModel( );
expressionTree->setModel( mModel );
mProxyModel = new QgsExpressionItemSearhProxy();
mProxyModel->setSourceModel( mModel );
expressionTree->setModel( mProxyModel );

expressionTree->setContextMenuPolicy( Qt::CustomContextMenu );
connect( expressionTree, SIGNAL( customContextMenuRequested( const QPoint & ) ), this, SLOT( showContextMenu( const QPoint & ) ) );

this->registerItem("Operators","+"," + ");
this->registerItem("Operators","-"," -");
Expand Down Expand Up @@ -58,29 +62,26 @@ QgsExpressionBuilderWidget::~QgsExpressionBuilderWidget()

}


void QgsExpressionBuilderWidget::on_mAllPushButton_clicked()
void QgsExpressionBuilderWidget::setLayer( QgsVectorLayer *layer )
{
// We don't use this yet.
// TODO
mLayer = layer;
}

void QgsExpressionBuilderWidget::on_expressionTree_clicked(const QModelIndex &index)
{
// Get the item
QgsExpressionItem* item = dynamic_cast<QgsExpressionItem*>(mModel->itemFromIndex(index));
QModelIndex idx = mProxyModel->mapToSource(index);
QgsExpressionItem* item = dynamic_cast<QgsExpressionItem*>(mModel->itemFromIndex( idx ));
if ( item == 0 )
return;

// If field item then we load a sample set of values from the field.
// Loading field values are handled with a
// right click so we just show the help.
if (item->getItemType() == QgsExpressionItem::Field)
{
mValueListWidget->show();
mValueListLabel->show();
int fieldIndex = mLayer->fieldNameIndex(item->text());
fillFieldValues(fieldIndex,10);
txtHelpText->setText("Double click to add field name to expression string. <br> " \
"Or double click an item in the value list to add it to the expression string.");
txtHelpText->setText( tr("Double click to add field name to expression string. <br> " \
"Or right click to select loading value options then " \
"double click an item in the value list to add it to the expression string."));
txtHelpText->setToolTip(txtHelpText->text());
}
else
Expand All @@ -96,7 +97,8 @@ void QgsExpressionBuilderWidget::on_expressionTree_clicked(const QModelIndex &in

void QgsExpressionBuilderWidget::on_expressionTree_doubleClicked(const QModelIndex &index)
{
QgsExpressionItem* item = dynamic_cast<QgsExpressionItem*>(mModel->itemFromIndex(index));
QModelIndex idx = mProxyModel->mapToSource(index);
QgsExpressionItem* item = dynamic_cast<QgsExpressionItem*>(mModel->itemFromIndex( idx ));
if (item == 0)
return;

Expand All @@ -110,10 +112,10 @@ void QgsExpressionBuilderWidget::on_expressionTree_doubleClicked(const QModelInd

void QgsExpressionBuilderWidget::loadFieldNames()
{
// TODO We should really return a error the user of the widget that
// the there is no layer set.
if ( !mLayer )
{
return;
}

const QgsFieldMap fieldMap = mLayer->pendingFields();
QgsFieldMap::const_iterator fieldIt = fieldMap.constBegin();
Expand All @@ -126,6 +128,12 @@ void QgsExpressionBuilderWidget::loadFieldNames()

void QgsExpressionBuilderWidget::fillFieldValues(int fieldIndex, int countLimit)
{
// TODO We should really return a error the user of the widget that
// the there is no layer set.
if ( !mLayer )
return;

// TODO We should thread this so that we don't hold the user up if the layer is massive.
mValueListWidget->clear();
mValueListWidget->setUpdatesEnabled( false );
mValueListWidget->blockSignals( true );
Expand Down Expand Up @@ -199,5 +207,59 @@ void QgsExpressionBuilderWidget::on_txtExpressionString_textChanged()
}
}

void QgsExpressionBuilderWidget::on_txtSearchEdit_textChanged()
{
mProxyModel->setFilterWildcard( txtSearchEdit->text() );
if ( txtSearchEdit->text().isEmpty() )
expressionTree->collapseAll();
else
expressionTree->expandAll();
}

void QgsExpressionBuilderWidget::showContextMenu( const QPoint & pt)
{
QModelIndex idx = expressionTree->indexAt( pt );
idx = mProxyModel->mapToSource( idx );
QgsExpressionItem* item = dynamic_cast<QgsExpressionItem*>(mModel->itemFromIndex( idx ));
if ( !item )
return;

if (item->getItemType() == QgsExpressionItem::Field)
{
QMenu* menu = new QMenu( this );
menu->addAction( tr( "Load top 10 unique values" ), this, SLOT( loadSampleValues()) );
menu->addAction( tr( "Load all unique values" ), this, SLOT( loadAllValues() ) );
menu->popup( expressionTree->mapToGlobal( pt ) );
}
}

void QgsExpressionBuilderWidget::loadSampleValues()
{
QModelIndex idx = mProxyModel->mapToSource(expressionTree->currentIndex());
QgsExpressionItem* item = dynamic_cast<QgsExpressionItem*>(mModel->itemFromIndex( idx ));
// TODO We should really return a error the user of the widget that
// the there is no layer set.
if ( !mLayer )
return;

mValueListWidget->show();
mValueListLabel->show();
int fieldIndex = mLayer->fieldNameIndex(item->text());
fillFieldValues(fieldIndex,10);
}

void QgsExpressionBuilderWidget::loadAllValues()
{
QModelIndex idx = mProxyModel->mapToSource(expressionTree->currentIndex());
QgsExpressionItem* item = dynamic_cast<QgsExpressionItem*>(mModel->itemFromIndex( idx ));
// TODO We should really return a error the user of the widget that
// the there is no layer set.
if ( !mLayer )
return;

mValueListWidget->show();
mValueListLabel->show();
int fieldIndex = mLayer->fieldNameIndex(item->text());
fillFieldValues(fieldIndex,-1);
}

30 changes: 28 additions & 2 deletions src/gui/qgsexpressionbuilder.h
Expand Up @@ -22,6 +22,23 @@

#include "QStandardItemModel"
#include "QStandardItem"
#include "QSortFilterProxyModel"

class QgsExpressionItemSearhProxy : public QSortFilterProxyModel
{
public:
QgsExpressionItemSearhProxy()
{
}

bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
if (source_parent == qobject_cast<QStandardItemModel*>(sourceModel())->invisibleRootItem()->index())
return true;

QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
}
};

class QgsExpressionItem : public QStandardItem
{
Expand Down Expand Up @@ -82,9 +99,14 @@ class QgsExpressionItem : public QStandardItem
class QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExpressionBuilder {
Q_OBJECT
public:
QgsExpressionBuilderWidget(QgsVectorLayer * layer);
QgsExpressionBuilderWidget(QWidget *parent);
~QgsExpressionBuilderWidget();

/** Sets layer in order to get the fields and values
* @note this needs to be called before calling loadFieldNames().
*/
void setLayer( QgsVectorLayer* layer );

/** Loads all the field names from the layer.
* @remarks Should this really be public couldn't we just do this for the user?
*/
Expand Down Expand Up @@ -115,10 +137,13 @@ class QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExpressionBuil
bool hasExpressionError();

public slots:
void on_mAllPushButton_clicked();
void on_expressionTree_clicked(const QModelIndex &index);
void on_expressionTree_doubleClicked(const QModelIndex &index);
void on_txtExpressionString_textChanged();
void on_txtSearchEdit_textChanged();
void showContextMenu( const QPoint & );
void loadSampleValues();
void loadAllValues();

signals:
void expressionParsed(bool isVaild);
Expand All @@ -128,6 +153,7 @@ public slots:

QgsVectorLayer *mLayer;
QStandardItemModel *mModel;
QgsExpressionItemSearhProxy *mProxyModel;
QMap<QString, QgsExpressionItem*> mExpressionGroups;
};

Expand Down
37 changes: 37 additions & 0 deletions src/gui/qgsexpressionbuilderdialog.cpp
@@ -0,0 +1,37 @@
#include "qgsexpressionbuilderdialog.h"
#include <QSettings>

QgsExpressionBuilderDialog::QgsExpressionBuilderDialog( QgsVectorLayer* layer, QString startText, QWidget* parent)
:QDialog( parent )
{
setupUi( this );

QPushButton* okButuon = buttonBox->button(QDialogButtonBox::Ok);
connect(builder, SIGNAL(expressionParsed(bool)), okButuon, SLOT(setEnabled(bool)));

builder->setLayer( layer );
builder->setExpressionString(startText);
builder->loadFieldNames();

QSettings settings;
restoreGeometry( settings.value( "/Windows/ExpressionBuilderDialog/geometry" ).toByteArray() );
}

QgsExpressionBuilderWidget* QgsExpressionBuilderDialog::expressionBuilder()
{
return builder;
}

void QgsExpressionBuilderDialog::setExpressionText( QString text )
{
builder->setExpressionString( text );
}

void QgsExpressionBuilderDialog::closeEvent( QCloseEvent *event )
{
QDialog::closeEvent( event );

// TODO Work out why this is not working yet.
QSettings settings;
settings.setValue( "/Windows/ExpressionBuilderDialog/geometry", saveGeometry() );
}
24 changes: 24 additions & 0 deletions src/gui/qgsexpressionbuilderdialog.h
@@ -0,0 +1,24 @@
#ifndef QGSEXPRESSIONBUILDERDIALOG_H
#define QGSEXPRESSIONBUILDERDIALOG_H

#include <QDialog>
#include "ui_qgsexpressionbuilderdialogbase.h"

class QgsExpressionBuilderDialog : public QDialog, private Ui::QgsExpressionBuilderDialogBase
{
public:
QgsExpressionBuilderDialog( QgsVectorLayer* layer, QString startText = "", QWidget* parent = NULL);

/** The builder widget that is used by the dialog */
QgsExpressionBuilderWidget* expressionBuilder();

void setExpressionText( QString text );
protected:
/**
* Handle closing of the window
* @param event unused
*/
void closeEvent( QCloseEvent * event );
};

#endif

0 comments on commit 6e41cc4

Please sign in to comment.