Skip to content

Commit fd50833

Browse files
committed
Add missing files; add more operators
1 parent 913e7e7 commit fd50833

File tree

2 files changed

+507
-0
lines changed

2 files changed

+507
-0
lines changed
Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
/***************************************************************************
2+
qgisexpressionbuilder.cpp - A genric expression string builder widget.
3+
--------------------------------------
4+
Date : 29-May-2011
5+
Copyright : (C) 2011 by Nathan Woodrow
6+
Email : nathan.woodrow at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include "qgsexpressionbuilderwidget.h"
17+
#include "qgslogger.h"
18+
#include "qgsexpression.h"
19+
#include "qgsmessageviewer.h"
20+
21+
#include <QMenu>
22+
23+
QgsExpressionBuilderWidget::QgsExpressionBuilderWidget(QWidget *parent)
24+
: QWidget(parent)
25+
{
26+
setupUi(this);
27+
28+
mValueListWidget->hide();
29+
mValueListLabel->hide();
30+
31+
mModel = new QStandardItemModel( );
32+
mProxyModel = new QgsExpressionItemSearhProxy();
33+
mProxyModel->setSourceModel( mModel );
34+
expressionTree->setModel( mProxyModel );
35+
36+
expressionTree->setContextMenuPolicy( Qt::CustomContextMenu );
37+
connect( expressionTree, SIGNAL( customContextMenuRequested( const QPoint & ) ), this, SLOT( showContextMenu( const QPoint & ) ) );
38+
connect( btnPlusPushButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
39+
connect( btnMinusPushButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
40+
connect( btnDividePushButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
41+
connect( btnMultiplyPushButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
42+
connect( btnExpButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
43+
connect( btnConcatButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
44+
connect( btnOpenBracketPushButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
45+
connect( btnCloseBracketPushButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
46+
47+
48+
// TODO Can we move this stuff to QgsExpression, like the functions?
49+
this->registerItem("Operators","+"," + ");
50+
this->registerItem("Operators","-"," -");
51+
this->registerItem("Operators","*"," * ");
52+
this->registerItem("Operators","/"," / ");
53+
this->registerItem("Operators","%"," % ");
54+
this->registerItem("Operators","^"," ^ ");
55+
this->registerItem("Operators","="," = ");
56+
this->registerItem("Operators",">"," > ");
57+
this->registerItem("Operators","<"," < ");
58+
this->registerItem("Operators","<>"," <> ");
59+
this->registerItem("Operators","<="," <= ");
60+
this->registerItem("Operators",">="," >= ");
61+
this->registerItem("Operators","||"," || ","<b>|| (String Concatenation)</b> "\
62+
"<br> Joins two values together into a string " \
63+
"<br> <i>Usage:</i><br>'Dia' || Diameter");
64+
this->registerItem("Operators","LIKE"," LIKE ");
65+
this->registerItem("Operators","ILIKE"," ILIKE ");
66+
this->registerItem("Operators","IS"," IS NOT ");
67+
this->registerItem("Operators","OR"," OR ");
68+
this->registerItem("Operators","AND"," AND ");
69+
this->registerItem("Operators","NOT"," NOT ");
70+
71+
72+
// Load the fuctions from the QgsExpression class
73+
int count = QgsExpression::functionCount();
74+
for ( int i = 0; i < count; i++ )
75+
{
76+
QgsExpression::FunctionDef func = QgsExpression::BuiltinFunctions[i];
77+
QString name = func.mName;
78+
if ( func.mParams >= 1 )
79+
name += "(";
80+
this->registerItem(func.mGroup,func.mName, " " + name + " ", func.mHelpText);
81+
};
82+
}
83+
84+
85+
QgsExpressionBuilderWidget::~QgsExpressionBuilderWidget()
86+
{
87+
88+
}
89+
90+
void QgsExpressionBuilderWidget::setLayer( QgsVectorLayer *layer )
91+
{
92+
mLayer = layer;
93+
}
94+
95+
void QgsExpressionBuilderWidget::on_expressionTree_clicked(const QModelIndex &index)
96+
{
97+
// Get the item
98+
QModelIndex idx = mProxyModel->mapToSource(index);
99+
QgsExpressionItem* item = dynamic_cast<QgsExpressionItem*>(mModel->itemFromIndex( idx ));
100+
if ( item == 0 )
101+
return;
102+
103+
// Loading field values are handled with a
104+
// right click so we just show the help.
105+
if (item->getItemType() == QgsExpressionItem::Field)
106+
{
107+
txtHelpText->setText( tr("Double click to add field name to expression string. <br> " \
108+
"Or right click to select loading value options then " \
109+
"double click an item in the value list to add it to the expression string."));
110+
txtHelpText->setToolTip(txtHelpText->text());
111+
}
112+
else
113+
{
114+
// Show the help for the current item.
115+
mValueListWidget->hide();
116+
mValueListLabel->hide();
117+
mValueListWidget->clear();
118+
txtHelpText->setText(item->getHelpText());
119+
txtHelpText->setToolTip(txtHelpText->text());
120+
}
121+
}
122+
123+
void QgsExpressionBuilderWidget::on_expressionTree_doubleClicked(const QModelIndex &index)
124+
{
125+
QModelIndex idx = mProxyModel->mapToSource(index);
126+
QgsExpressionItem* item = dynamic_cast<QgsExpressionItem*>(mModel->itemFromIndex( idx ));
127+
if (item == 0)
128+
return;
129+
130+
// Don't handle the double click it we are on a header node.
131+
if (item->getItemType() == QgsExpressionItem::Header)
132+
return;
133+
134+
// Insert the expression text.
135+
txtExpressionString->insertPlainText(item->getExpressionText());
136+
}
137+
138+
void QgsExpressionBuilderWidget::loadFieldNames()
139+
{
140+
// TODO We should really return a error the user of the widget that
141+
// the there is no layer set.
142+
if ( !mLayer )
143+
return;
144+
145+
const QgsFieldMap fieldMap = mLayer->pendingFields();
146+
QgsFieldMap::const_iterator fieldIt = fieldMap.constBegin();
147+
for ( ; fieldIt != fieldMap.constEnd(); ++fieldIt )
148+
{
149+
QString fieldName = fieldIt.value().name();
150+
this->registerItem("Fields", fieldName, " " + fieldName + " ","", QgsExpressionItem::Field);
151+
}
152+
}
153+
154+
void QgsExpressionBuilderWidget::fillFieldValues(int fieldIndex, int countLimit)
155+
{
156+
// TODO We should really return a error the user of the widget that
157+
// the there is no layer set.
158+
if ( !mLayer )
159+
return;
160+
161+
// TODO We should thread this so that we don't hold the user up if the layer is massive.
162+
mValueListWidget->clear();
163+
mValueListWidget->setUpdatesEnabled( false );
164+
mValueListWidget->blockSignals( true );
165+
166+
QList<QVariant> values;
167+
mLayer->uniqueValues( fieldIndex, values, countLimit );
168+
foreach(QVariant value, values)
169+
{
170+
mValueListWidget->addItem(value.toString());
171+
}
172+
173+
mValueListWidget->setUpdatesEnabled( true );
174+
mValueListWidget->blockSignals( false );
175+
}
176+
177+
void QgsExpressionBuilderWidget::registerItem(QString group,
178+
QString label,
179+
QString expressionText,
180+
QString helpText,
181+
QgsExpressionItem::ItemType type)
182+
{
183+
QgsExpressionItem* item = new QgsExpressionItem(label,expressionText, helpText, type);
184+
// Look up the group and insert the new function.
185+
if (mExpressionGroups.contains(group))
186+
{
187+
QgsExpressionItem* groupNode = mExpressionGroups.value(group);
188+
groupNode->appendRow(item);
189+
}
190+
else
191+
{
192+
// If the group doesn't exsit yet we make it first.
193+
QgsExpressionItem* newgroupNode = new QgsExpressionItem(group,"", QgsExpressionItem::Header);
194+
newgroupNode->appendRow(item);
195+
mModel->appendRow(newgroupNode);
196+
mExpressionGroups.insert(group , newgroupNode );
197+
}
198+
}
199+
200+
QString QgsExpressionBuilderWidget::getExpressionString()
201+
{
202+
return this->txtExpressionString->toPlainText();
203+
}
204+
205+
void QgsExpressionBuilderWidget::setExpressionString(const QString expressionString)
206+
{
207+
this->txtExpressionString->setPlainText(expressionString);
208+
}
209+
210+
void QgsExpressionBuilderWidget::on_txtExpressionString_textChanged()
211+
{
212+
QString text = this->txtExpressionString->toPlainText();
213+
214+
// If the string is empty the expression will still "fail" although
215+
// we don't show the user an error as it will be confusing.
216+
if ( text.isEmpty() )
217+
{
218+
this->lblPreview->setText("");
219+
this->lblPreview->setStyleSheet("");
220+
this->txtExpressionString->setToolTip("");
221+
this->lblPreview->setToolTip("");
222+
// Return false for isVaild because a null expression is still invaild.
223+
emit expressionParsed(false);
224+
return;
225+
}
226+
227+
QgsExpression exp( text );
228+
229+
// TODO We could do this without a layer.
230+
// Maybe just calling exp.evaluate()?
231+
if ( mLayer )
232+
{
233+
// TODO We should really cache the feature.
234+
QgsFeature feature;
235+
mLayer->featureAtId( 0 , feature );
236+
QVariant value = exp.evaluate( &feature, mLayer->pendingFields() );
237+
238+
if (!exp.hasEvalError())
239+
lblPreview->setText( value.toString() );
240+
}
241+
242+
if ( exp.hasParserError() || exp.hasEvalError())
243+
{
244+
QString tooltip = "<b>Parser Error:</b> <br>" + exp.parserErrorString();
245+
if (exp.hasEvalError())
246+
tooltip += "<br><br> <b>Eval Error:</b> <br>" + exp.evalErrorString();
247+
248+
this->lblPreview->setText( "Expression is invaild <a href=""more"">(more info)</a>" );
249+
this->lblPreview->setStyleSheet("color: rgba(255, 6, 10, 255);");
250+
this->txtExpressionString->setToolTip(tooltip);
251+
this->lblPreview->setToolTip(tooltip);
252+
emit expressionParsed(false);
253+
return;
254+
}
255+
else
256+
{
257+
this->lblPreview->setStyleSheet("");
258+
this->txtExpressionString->setToolTip("");
259+
this->lblPreview->setToolTip("");
260+
emit expressionParsed(true);
261+
}
262+
}
263+
264+
void QgsExpressionBuilderWidget::on_txtSearchEdit_textChanged()
265+
{
266+
mProxyModel->setFilterWildcard( txtSearchEdit->text() );
267+
if ( txtSearchEdit->text().isEmpty() )
268+
expressionTree->collapseAll();
269+
else
270+
expressionTree->expandAll();
271+
}
272+
273+
void QgsExpressionBuilderWidget::on_lblPreview_linkActivated(QString link)
274+
{
275+
QgsMessageViewer * mv = new QgsMessageViewer( this );
276+
mv->setWindowTitle( "More info on expression error" );
277+
mv->setMessageAsHtml( this->txtExpressionString->toolTip());
278+
mv->exec();
279+
}
280+
281+
void QgsExpressionBuilderWidget::on_mValueListWidget_itemDoubleClicked(QListWidgetItem *item)
282+
{
283+
txtExpressionString->insertPlainText( " " + item->text() + " " );
284+
}
285+
286+
void QgsExpressionBuilderWidget::operatorButtonClicked()
287+
{
288+
QPushButton* button = dynamic_cast<QPushButton*>( sender() );
289+
txtExpressionString->insertPlainText( " " + button->text() + " " );
290+
}
291+
292+
void QgsExpressionBuilderWidget::showContextMenu( const QPoint & pt)
293+
{
294+
QModelIndex idx = expressionTree->indexAt( pt );
295+
idx = mProxyModel->mapToSource( idx );
296+
QgsExpressionItem* item = dynamic_cast<QgsExpressionItem*>(mModel->itemFromIndex( idx ));
297+
if ( !item )
298+
return;
299+
300+
if (item->getItemType() == QgsExpressionItem::Field)
301+
{
302+
QMenu* menu = new QMenu( this );
303+
menu->addAction( tr( "Load top 10 unique values" ), this, SLOT( loadSampleValues()) );
304+
menu->addAction( tr( "Load all unique values" ), this, SLOT( loadAllValues() ) );
305+
menu->popup( expressionTree->mapToGlobal( pt ) );
306+
}
307+
}
308+
309+
void QgsExpressionBuilderWidget::loadSampleValues()
310+
{
311+
QModelIndex idx = mProxyModel->mapToSource(expressionTree->currentIndex());
312+
QgsExpressionItem* item = dynamic_cast<QgsExpressionItem*>(mModel->itemFromIndex( idx ));
313+
// TODO We should really return a error the user of the widget that
314+
// the there is no layer set.
315+
if ( !mLayer )
316+
return;
317+
318+
mValueListWidget->show();
319+
mValueListLabel->show();
320+
int fieldIndex = mLayer->fieldNameIndex(item->text());
321+
fillFieldValues(fieldIndex,10);
322+
}
323+
324+
void QgsExpressionBuilderWidget::loadAllValues()
325+
{
326+
QModelIndex idx = mProxyModel->mapToSource(expressionTree->currentIndex());
327+
QgsExpressionItem* item = dynamic_cast<QgsExpressionItem*>(mModel->itemFromIndex( idx ));
328+
// TODO We should really return a error the user of the widget that
329+
// the there is no layer set.
330+
if ( !mLayer )
331+
return;
332+
333+
mValueListWidget->show();
334+
mValueListLabel->show();
335+
int fieldIndex = mLayer->fieldNameIndex(item->text());
336+
fillFieldValues(fieldIndex,-1);
337+
}
338+

0 commit comments

Comments
 (0)