mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-25 00:58:06 -05:00
Add missing files; add more operators
This commit is contained in:
parent
913e7e7866
commit
fd50833e83
338
src/gui/qgsexpressionbuilderwidget.cpp
Normal file
338
src/gui/qgsexpressionbuilderwidget.cpp
Normal file
@ -0,0 +1,338 @@
|
||||
/***************************************************************************
|
||||
qgisexpressionbuilder.cpp - A genric expression string builder widget.
|
||||
--------------------------------------
|
||||
Date : 29-May-2011
|
||||
Copyright : (C) 2011 by Nathan Woodrow
|
||||
Email : nathan.woodrow at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgsexpressionbuilderwidget.h"
|
||||
#include "qgslogger.h"
|
||||
#include "qgsexpression.h"
|
||||
#include "qgsmessageviewer.h"
|
||||
|
||||
#include <QMenu>
|
||||
|
||||
QgsExpressionBuilderWidget::QgsExpressionBuilderWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
setupUi(this);
|
||||
|
||||
mValueListWidget->hide();
|
||||
mValueListLabel->hide();
|
||||
|
||||
mModel = new QStandardItemModel( );
|
||||
mProxyModel = new QgsExpressionItemSearhProxy();
|
||||
mProxyModel->setSourceModel( mModel );
|
||||
expressionTree->setModel( mProxyModel );
|
||||
|
||||
expressionTree->setContextMenuPolicy( Qt::CustomContextMenu );
|
||||
connect( expressionTree, SIGNAL( customContextMenuRequested( const QPoint & ) ), this, SLOT( showContextMenu( const QPoint & ) ) );
|
||||
connect( btnPlusPushButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
|
||||
connect( btnMinusPushButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
|
||||
connect( btnDividePushButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
|
||||
connect( btnMultiplyPushButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
|
||||
connect( btnExpButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
|
||||
connect( btnConcatButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
|
||||
connect( btnOpenBracketPushButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
|
||||
connect( btnCloseBracketPushButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
|
||||
|
||||
|
||||
// TODO Can we move this stuff to QgsExpression, like the functions?
|
||||
this->registerItem("Operators","+"," + ");
|
||||
this->registerItem("Operators","-"," -");
|
||||
this->registerItem("Operators","*"," * ");
|
||||
this->registerItem("Operators","/"," / ");
|
||||
this->registerItem("Operators","%"," % ");
|
||||
this->registerItem("Operators","^"," ^ ");
|
||||
this->registerItem("Operators","="," = ");
|
||||
this->registerItem("Operators",">"," > ");
|
||||
this->registerItem("Operators","<"," < ");
|
||||
this->registerItem("Operators","<>"," <> ");
|
||||
this->registerItem("Operators","<="," <= ");
|
||||
this->registerItem("Operators",">="," >= ");
|
||||
this->registerItem("Operators","||"," || ","<b>|| (String Concatenation)</b> "\
|
||||
"<br> Joins two values together into a string " \
|
||||
"<br> <i>Usage:</i><br>'Dia' || Diameter");
|
||||
this->registerItem("Operators","LIKE"," LIKE ");
|
||||
this->registerItem("Operators","ILIKE"," ILIKE ");
|
||||
this->registerItem("Operators","IS"," IS NOT ");
|
||||
this->registerItem("Operators","OR"," OR ");
|
||||
this->registerItem("Operators","AND"," AND ");
|
||||
this->registerItem("Operators","NOT"," NOT ");
|
||||
|
||||
|
||||
// Load the fuctions from the QgsExpression class
|
||||
int count = QgsExpression::functionCount();
|
||||
for ( int i = 0; i < count; i++ )
|
||||
{
|
||||
QgsExpression::FunctionDef func = QgsExpression::BuiltinFunctions[i];
|
||||
QString name = func.mName;
|
||||
if ( func.mParams >= 1 )
|
||||
name += "(";
|
||||
this->registerItem(func.mGroup,func.mName, " " + name + " ", func.mHelpText);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
QgsExpressionBuilderWidget::~QgsExpressionBuilderWidget()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void QgsExpressionBuilderWidget::setLayer( QgsVectorLayer *layer )
|
||||
{
|
||||
mLayer = layer;
|
||||
}
|
||||
|
||||
void QgsExpressionBuilderWidget::on_expressionTree_clicked(const QModelIndex &index)
|
||||
{
|
||||
// Get the item
|
||||
QModelIndex idx = mProxyModel->mapToSource(index);
|
||||
QgsExpressionItem* item = dynamic_cast<QgsExpressionItem*>(mModel->itemFromIndex( idx ));
|
||||
if ( item == 0 )
|
||||
return;
|
||||
|
||||
// Loading field values are handled with a
|
||||
// right click so we just show the help.
|
||||
if (item->getItemType() == QgsExpressionItem::Field)
|
||||
{
|
||||
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
|
||||
{
|
||||
// Show the help for the current item.
|
||||
mValueListWidget->hide();
|
||||
mValueListLabel->hide();
|
||||
mValueListWidget->clear();
|
||||
txtHelpText->setText(item->getHelpText());
|
||||
txtHelpText->setToolTip(txtHelpText->text());
|
||||
}
|
||||
}
|
||||
|
||||
void QgsExpressionBuilderWidget::on_expressionTree_doubleClicked(const QModelIndex &index)
|
||||
{
|
||||
QModelIndex idx = mProxyModel->mapToSource(index);
|
||||
QgsExpressionItem* item = dynamic_cast<QgsExpressionItem*>(mModel->itemFromIndex( idx ));
|
||||
if (item == 0)
|
||||
return;
|
||||
|
||||
// Don't handle the double click it we are on a header node.
|
||||
if (item->getItemType() == QgsExpressionItem::Header)
|
||||
return;
|
||||
|
||||
// Insert the expression text.
|
||||
txtExpressionString->insertPlainText(item->getExpressionText());
|
||||
}
|
||||
|
||||
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();
|
||||
for ( ; fieldIt != fieldMap.constEnd(); ++fieldIt )
|
||||
{
|
||||
QString fieldName = fieldIt.value().name();
|
||||
this->registerItem("Fields", fieldName, " " + fieldName + " ","", QgsExpressionItem::Field);
|
||||
}
|
||||
}
|
||||
|
||||
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 );
|
||||
|
||||
QList<QVariant> values;
|
||||
mLayer->uniqueValues( fieldIndex, values, countLimit );
|
||||
foreach(QVariant value, values)
|
||||
{
|
||||
mValueListWidget->addItem(value.toString());
|
||||
}
|
||||
|
||||
mValueListWidget->setUpdatesEnabled( true );
|
||||
mValueListWidget->blockSignals( false );
|
||||
}
|
||||
|
||||
void QgsExpressionBuilderWidget::registerItem(QString group,
|
||||
QString label,
|
||||
QString expressionText,
|
||||
QString helpText,
|
||||
QgsExpressionItem::ItemType type)
|
||||
{
|
||||
QgsExpressionItem* item = new QgsExpressionItem(label,expressionText, helpText, type);
|
||||
// Look up the group and insert the new function.
|
||||
if (mExpressionGroups.contains(group))
|
||||
{
|
||||
QgsExpressionItem* groupNode = mExpressionGroups.value(group);
|
||||
groupNode->appendRow(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the group doesn't exsit yet we make it first.
|
||||
QgsExpressionItem* newgroupNode = new QgsExpressionItem(group,"", QgsExpressionItem::Header);
|
||||
newgroupNode->appendRow(item);
|
||||
mModel->appendRow(newgroupNode);
|
||||
mExpressionGroups.insert(group , newgroupNode );
|
||||
}
|
||||
}
|
||||
|
||||
QString QgsExpressionBuilderWidget::getExpressionString()
|
||||
{
|
||||
return this->txtExpressionString->toPlainText();
|
||||
}
|
||||
|
||||
void QgsExpressionBuilderWidget::setExpressionString(const QString expressionString)
|
||||
{
|
||||
this->txtExpressionString->setPlainText(expressionString);
|
||||
}
|
||||
|
||||
void QgsExpressionBuilderWidget::on_txtExpressionString_textChanged()
|
||||
{
|
||||
QString text = this->txtExpressionString->toPlainText();
|
||||
|
||||
// If the string is empty the expression will still "fail" although
|
||||
// we don't show the user an error as it will be confusing.
|
||||
if ( text.isEmpty() )
|
||||
{
|
||||
this->lblPreview->setText("");
|
||||
this->lblPreview->setStyleSheet("");
|
||||
this->txtExpressionString->setToolTip("");
|
||||
this->lblPreview->setToolTip("");
|
||||
// Return false for isVaild because a null expression is still invaild.
|
||||
emit expressionParsed(false);
|
||||
return;
|
||||
}
|
||||
|
||||
QgsExpression exp( text );
|
||||
|
||||
// TODO We could do this without a layer.
|
||||
// Maybe just calling exp.evaluate()?
|
||||
if ( mLayer )
|
||||
{
|
||||
// TODO We should really cache the feature.
|
||||
QgsFeature feature;
|
||||
mLayer->featureAtId( 0 , feature );
|
||||
QVariant value = exp.evaluate( &feature, mLayer->pendingFields() );
|
||||
|
||||
if (!exp.hasEvalError())
|
||||
lblPreview->setText( value.toString() );
|
||||
}
|
||||
|
||||
if ( exp.hasParserError() || exp.hasEvalError())
|
||||
{
|
||||
QString tooltip = "<b>Parser Error:</b> <br>" + exp.parserErrorString();
|
||||
if (exp.hasEvalError())
|
||||
tooltip += "<br><br> <b>Eval Error:</b> <br>" + exp.evalErrorString();
|
||||
|
||||
this->lblPreview->setText( "Expression is invaild <a href=""more"">(more info)</a>" );
|
||||
this->lblPreview->setStyleSheet("color: rgba(255, 6, 10, 255);");
|
||||
this->txtExpressionString->setToolTip(tooltip);
|
||||
this->lblPreview->setToolTip(tooltip);
|
||||
emit expressionParsed(false);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->lblPreview->setStyleSheet("");
|
||||
this->txtExpressionString->setToolTip("");
|
||||
this->lblPreview->setToolTip("");
|
||||
emit expressionParsed(true);
|
||||
}
|
||||
}
|
||||
|
||||
void QgsExpressionBuilderWidget::on_txtSearchEdit_textChanged()
|
||||
{
|
||||
mProxyModel->setFilterWildcard( txtSearchEdit->text() );
|
||||
if ( txtSearchEdit->text().isEmpty() )
|
||||
expressionTree->collapseAll();
|
||||
else
|
||||
expressionTree->expandAll();
|
||||
}
|
||||
|
||||
void QgsExpressionBuilderWidget::on_lblPreview_linkActivated(QString link)
|
||||
{
|
||||
QgsMessageViewer * mv = new QgsMessageViewer( this );
|
||||
mv->setWindowTitle( "More info on expression error" );
|
||||
mv->setMessageAsHtml( this->txtExpressionString->toolTip());
|
||||
mv->exec();
|
||||
}
|
||||
|
||||
void QgsExpressionBuilderWidget::on_mValueListWidget_itemDoubleClicked(QListWidgetItem *item)
|
||||
{
|
||||
txtExpressionString->insertPlainText( " " + item->text() + " " );
|
||||
}
|
||||
|
||||
void QgsExpressionBuilderWidget::operatorButtonClicked()
|
||||
{
|
||||
QPushButton* button = dynamic_cast<QPushButton*>( sender() );
|
||||
txtExpressionString->insertPlainText( " " + button->text() + " " );
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
169
src/gui/qgsexpressionbuilderwidget.h
Normal file
169
src/gui/qgsexpressionbuilderwidget.h
Normal file
@ -0,0 +1,169 @@
|
||||
/***************************************************************************
|
||||
qgisexpressionbuilder.h - A genric expression string builder widget.
|
||||
--------------------------------------
|
||||
Date : 29-May-2011
|
||||
Copyright : (C) 2006 by Nathan Woodrow
|
||||
Email : nathan.woodrow at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSEXPRESSIONBUILDER_H
|
||||
#define QGSEXPRESSIONBUILDER_H
|
||||
|
||||
#include <QWidget>
|
||||
#include "ui_qgsexpressionbuilder.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
|
||||
#include "QStandardItemModel"
|
||||
#include "QStandardItem"
|
||||
#include "QSortFilterProxyModel"
|
||||
|
||||
/** Search proxy used to filter the QgsExpressionBuilderWidget tree.
|
||||
* The default search for a tree model only searches top level this will handle one
|
||||
* level down
|
||||
*/
|
||||
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;
|
||||
|
||||
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
|
||||
}
|
||||
};
|
||||
|
||||
/** An expression item that can be used in the QgsExpressionBuilderWidget tree.
|
||||
*/
|
||||
class QgsExpressionItem : public QStandardItem
|
||||
{
|
||||
public:
|
||||
enum ItemType
|
||||
{
|
||||
Header,
|
||||
Field,
|
||||
ExpressionNode
|
||||
};
|
||||
|
||||
QgsExpressionItem(QString label,
|
||||
QString expressionText,
|
||||
QString helpText,
|
||||
QgsExpressionItem::ItemType itemType = ExpressionNode)
|
||||
: QStandardItem(label)
|
||||
{
|
||||
mExpressionText = expressionText;
|
||||
mHelpText = helpText;
|
||||
mType = itemType;
|
||||
}
|
||||
|
||||
QgsExpressionItem(QString label,
|
||||
QString expressionText,
|
||||
QgsExpressionItem::ItemType itemType = ExpressionNode)
|
||||
: QStandardItem(label)
|
||||
{
|
||||
mExpressionText = expressionText;
|
||||
mType = itemType;
|
||||
}
|
||||
|
||||
QString getExpressionText() { return mExpressionText; }
|
||||
|
||||
/** Get the help text that is associated with this expression item.
|
||||
*
|
||||
* @return The help text.
|
||||
*/
|
||||
QString getHelpText() { return mHelpText; }
|
||||
/** Set the help text for the current item
|
||||
*
|
||||
* @note The help text can be set as a html string.
|
||||
*/
|
||||
void setHelpText(QString helpText) { mHelpText = helpText; }
|
||||
|
||||
/** Get the type of expression item eg header, field, ExpressionNode.
|
||||
*
|
||||
* @return The QgsExpressionItem::ItemType
|
||||
*/
|
||||
QgsExpressionItem::ItemType getItemType() { return mType ; }
|
||||
|
||||
private:
|
||||
QString mExpressionText;
|
||||
QString mHelpText;
|
||||
QgsExpressionItem::ItemType mType;
|
||||
};
|
||||
|
||||
/** A reusable widget that can be used to build a expression string.
|
||||
* See QgsExpressionBuilderDialog for exmaple of usage.
|
||||
*/
|
||||
class QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExpressionBuilderWidgetBase {
|
||||
Q_OBJECT
|
||||
public:
|
||||
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?
|
||||
*/
|
||||
void loadFieldNames();
|
||||
|
||||
/** Gets the expression string that has been set in the expression area.
|
||||
* @returns The expression as a string. */
|
||||
QString getExpressionString();
|
||||
|
||||
/** Sets the expression string for the widget */
|
||||
void setExpressionString(const QString expressionString);
|
||||
|
||||
/** Registers a node item for the expression builder.
|
||||
* @param group The group the item will be show in the tree view. If the group doesn't exsit it will be created.
|
||||
* @param label The label that is show to the user for the item in the tree.
|
||||
* @param expressionText The text that is inserted into the expression area when the user double clicks on the item.
|
||||
* @param helpText The help text that the user will see when item is selected.
|
||||
* @param type The type of the expression item.
|
||||
*/
|
||||
void registerItem(QString group, QString label,QString expressionText,
|
||||
QString helpText = "",
|
||||
QgsExpressionItem::ItemType type = QgsExpressionItem::ExpressionNode);
|
||||
|
||||
public slots:
|
||||
void on_expressionTree_clicked(const QModelIndex &index);
|
||||
void on_expressionTree_doubleClicked(const QModelIndex &index);
|
||||
void on_txtExpressionString_textChanged();
|
||||
void on_txtSearchEdit_textChanged();
|
||||
void on_lblPreview_linkActivated(QString link);
|
||||
void on_mValueListWidget_itemDoubleClicked(QListWidgetItem* item);
|
||||
void operatorButtonClicked();
|
||||
void showContextMenu( const QPoint & );
|
||||
void loadSampleValues();
|
||||
void loadAllValues();
|
||||
|
||||
signals:
|
||||
/** Emited when the user changes the expression in the widget.
|
||||
* Users of this widget should connect to this signal to decide if to let the user
|
||||
* continue.
|
||||
* @param isVaild Is true if the expression the user has typed is vaild.
|
||||
*/
|
||||
void expressionParsed(bool isVaild);
|
||||
|
||||
private:
|
||||
void fillFieldValues(int fieldIndex, int countLimit);
|
||||
|
||||
QgsVectorLayer *mLayer;
|
||||
QStandardItemModel *mModel;
|
||||
QgsExpressionItemSearhProxy *mProxyModel;
|
||||
QMap<QString, QgsExpressionItem*> mExpressionGroups;
|
||||
QgsFeature* mFeature;
|
||||
};
|
||||
|
||||
#endif // QGSEXPRESSIONBUILDER_H
|
Loading…
x
Reference in New Issue
Block a user