Model/view-based tree of rules. Added basic unit test, GUI renderer test

This commit is contained in:
Martin Dobias 2012-01-21 12:45:20 +01:00
parent 83ec8104bd
commit 245e76daa4
13 changed files with 575 additions and 558 deletions

View File

@ -430,8 +430,10 @@ class QgsRuleBasedRendererV2 : QgsFeatureRendererV2
static QgsFeatureRendererV2* create( QDomElement& element ) /Factory/;
//! Constructor. Takes ownership of the defult symbol.
QgsRuleBasedRendererV2( QgsSymbolV2* defaultSymbol /Transfer/ = NULL );
//! Constructs the renderer from given tree of rules
QgsRuleBasedRendererV2( QgsRuleBasedRendererV2::Rule* root /Transfer/ );
//! Constructor for convenience. Creates a root rule and adds a default rule with symbol
QgsRuleBasedRendererV2( QgsSymbolV2* defaultSymbol /Transfer/ );
//! return symbol for current feature. Should not be used individually: there could be more symbols for a feature
virtual QgsSymbolV2* symbolForFeature( QgsFeature& feature );
@ -456,18 +458,6 @@ class QgsRuleBasedRendererV2 : QgsFeatureRendererV2
/////
//! return the total number of rules
int ruleCount();
//! get reference to rule at index (valid indexes: 0...count-1)
QgsRuleBasedRendererV2::Rule* ruleAt( int index );
//! add rule to the end of the list of rules
void addRule( QgsRuleBasedRendererV2::Rule* rule );
//! insert rule to a specific position of the list of rules
void insertRule( int index, QgsRuleBasedRendererV2::Rule* rule );
//! modify the rule at a specific position of the list of rules
void updateRuleAt( int index, QgsRuleBasedRendererV2::Rule* rule );
//! remove the rule at the specified index
void removeRuleAt( int index );
//////

View File

@ -29,7 +29,7 @@
QgsRuleBasedRendererV2::Rule::Rule( QgsSymbolV2* symbol, int scaleMinDenom, int scaleMaxDenom, QString filterExp, QString label, QString description )
: mSymbol( symbol ),
: mParent( NULL ), mSymbol( symbol ),
mScaleMinDenom( scaleMinDenom ), mScaleMaxDenom( scaleMaxDenom ),
mFilterExp( filterExp ), mLabel( label ), mDescription( description ),
mFilter( NULL )
@ -41,6 +41,8 @@ QgsRuleBasedRendererV2::Rule::~Rule()
{
delete mSymbol;
delete mFilter;
qDeleteAll( mChildren );
// do NOT delete parent
}
void QgsRuleBasedRendererV2::Rule::initFilter()
@ -145,9 +147,7 @@ QgsRuleBasedRendererV2::Rule* QgsRuleBasedRendererV2::Rule::clone() const
Rule* newrule = new Rule( sym, mScaleMinDenom, mScaleMaxDenom, mFilterExp, mLabel, mDescription );
// clone children
foreach( Rule* rule, mChildren )
{
newrule->mChildren.append( rule->clone() );
}
newrule->appendChild( rule->clone() );
return newrule;
}
@ -320,7 +320,7 @@ QgsRuleBasedRendererV2::Rule* QgsRuleBasedRendererV2::Rule::create( QDomElement&
{
Rule* childRule = create( childRuleElem, symbolMap );
if ( childRule )
rule->mChildren.append( childRule );
rule->appendChild( childRule );
else
QgsDebugMsg( "failed to init a child rule!" );
childRuleElem = childRuleElem.nextSiblingElement( "rule" );
@ -332,16 +332,16 @@ QgsRuleBasedRendererV2::Rule* QgsRuleBasedRendererV2::Rule::create( QDomElement&
/////////////////////
QgsRuleBasedRendererV2::QgsRuleBasedRendererV2( QgsRuleBasedRendererV2::Rule* root )
: QgsFeatureRendererV2( "RuleRenderer" ), mRootRule( root )
{
}
QgsRuleBasedRendererV2::QgsRuleBasedRendererV2( QgsSymbolV2* defaultSymbol )
: QgsFeatureRendererV2( "RuleRenderer" )
{
mRootRule = new Rule( NULL );
if ( defaultSymbol )
{
// add the default rule
mRootRule->children().append( new Rule( defaultSymbol ) );
}
mRootRule = new Rule( NULL ); // root has no symbol, no filter etc - just a container
mRootRule->appendChild( new Rule( defaultSymbol ) );
}
QgsRuleBasedRendererV2::~QgsRuleBasedRendererV2()
@ -454,9 +454,7 @@ QList<QString> QgsRuleBasedRendererV2::usedAttributes()
QgsFeatureRendererV2* QgsRuleBasedRendererV2::clone()
{
QgsRuleBasedRendererV2* r = new QgsRuleBasedRendererV2();
delete r->mRootRule;
r->mRootRule = mRootRule->clone();
QgsRuleBasedRendererV2* r = new QgsRuleBasedRendererV2( mRootRule->clone() );
r->setUsingSymbolLevels( usingSymbolLevels() );
setUsingSymbolLevels( usingSymbolLevels() );
@ -477,8 +475,8 @@ QDomElement QgsRuleBasedRendererV2::save( QDomDocument& doc )
QgsSymbolV2Map symbols;
QDomElement rulesElem = doc.createElement( "rules" );
rulesElem.appendChild( mRootRule->save( doc, symbols ) );
QDomElement rulesElem = mRootRule->save( doc, symbols );
rulesElem.setTagName( "rules" ); // instead of just "rule"
rendererElem.appendChild( rulesElem );
QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
@ -518,14 +516,11 @@ QgsFeatureRendererV2* QgsRuleBasedRendererV2::create( QDomElement& element )
QDomElement rulesElem = element.firstChildElement( "rules" );
QDomElement rootRuleElem = rulesElem.firstChildElement( "rule" );
Rule* root = Rule::create( rootRuleElem, symbolMap );
Rule* root = Rule::create( rulesElem, symbolMap );
if ( root == NULL )
return NULL;
QgsRuleBasedRendererV2* r = new QgsRuleBasedRendererV2();
delete r->mRootRule;
r->mRootRule = root;
QgsRuleBasedRendererV2* r = new QgsRuleBasedRendererV2( root );
// delete symbols if there are any more
QgsSymbolLayerV2Utils::clearSymbolMap( symbolMap );
@ -534,48 +529,10 @@ QgsFeatureRendererV2* QgsRuleBasedRendererV2::create( QDomElement& element )
}
int QgsRuleBasedRendererV2::ruleCount()
{
return mRootRule->children().count();
}
QgsRuleBasedRendererV2::Rule* QgsRuleBasedRendererV2::ruleAt( int index )
{
return mRootRule->children()[index];
}
void QgsRuleBasedRendererV2::addRule( QgsRuleBasedRendererV2::Rule* rule )
{
mRootRule->children().append( rule );
}
void QgsRuleBasedRendererV2::insertRule( int index, QgsRuleBasedRendererV2::Rule* rule )
{
mRootRule->children().insert( index, rule );
}
void QgsRuleBasedRendererV2::updateRuleAt( int index, QgsRuleBasedRendererV2::Rule* rule )
{
RuleList& rules = mRootRule->children();
delete rules[index]; // delete previous
rules[index] = rule;
}
void QgsRuleBasedRendererV2::removeRuleAt( int index )
{
delete mRootRule->children().takeAt( index );
}
void QgsRuleBasedRendererV2::swapRules( int index1, int index2 )
{
mRootRule->children().swap( index1, index2 );
}
#include "qgscategorizedsymbolrendererv2.h"
#include "qgsgraduatedsymbolrendererv2.h"
QgsRuleBasedRendererV2::RuleList QgsRuleBasedRendererV2::refineRuleCategories( QgsRuleBasedRendererV2::Rule* initialRule, QgsCategorizedSymbolRendererV2* r )
void QgsRuleBasedRendererV2::refineRuleCategories( QgsRuleBasedRendererV2::Rule* initialRule, QgsCategorizedSymbolRendererV2* r )
{
RuleList rules;
foreach( const QgsRendererCategoryV2& cat, r->categories() )
@ -588,14 +545,12 @@ QgsRuleBasedRendererV2::RuleList QgsRuleBasedRendererV2::refineRuleCategories( Q
filter = newfilter;
else
filter = QString( "(%1) AND (%2)" ).arg( filter ).arg( newfilter );
rules.append( new Rule( cat.symbol()->clone(), initialRule->scaleMinDenom(), initialRule->scaleMaxDenom(), filter, label, description ) );
initialRule->appendChild( new Rule( cat.symbol()->clone(), initialRule->scaleMinDenom(), initialRule->scaleMaxDenom(), filter, label, description ) );
}
return rules;
}
QgsRuleBasedRendererV2::RuleList QgsRuleBasedRendererV2::refineRuleRanges( QgsRuleBasedRendererV2::Rule* initialRule, QgsGraduatedSymbolRendererV2* r )
void QgsRuleBasedRendererV2::refineRuleRanges( QgsRuleBasedRendererV2::Rule* initialRule, QgsGraduatedSymbolRendererV2* r )
{
RuleList rules;
foreach( const QgsRendererRangeV2& rng, r->ranges() )
{
QString newfilter = QString( "%1 >= '%2' AND %1 <= '%3'" ).arg( r->classAttribute() ).arg( rng.lowerValue() ).arg( rng.upperValue() );
@ -606,15 +561,13 @@ QgsRuleBasedRendererV2::RuleList QgsRuleBasedRendererV2::refineRuleRanges( QgsRu
filter = newfilter;
else
filter = QString( "(%1) AND (%2)" ).arg( filter ).arg( newfilter );
rules.append( new Rule( rng.symbol()->clone(), initialRule->scaleMinDenom(), initialRule->scaleMaxDenom(), filter, label, description ) );
initialRule->appendChild( new Rule( rng.symbol()->clone(), initialRule->scaleMinDenom(), initialRule->scaleMaxDenom(), filter, label, description ) );
}
return rules;
}
QgsRuleBasedRendererV2::RuleList QgsRuleBasedRendererV2::refineRuleScales( QgsRuleBasedRendererV2::Rule* initialRule, QList<int> scales )
void QgsRuleBasedRendererV2::refineRuleScales( QgsRuleBasedRendererV2::Rule* initialRule, QList<int> scales )
{
qSort( scales ); // make sure the scales are in ascending order
RuleList rules;
int oldScale = initialRule->scaleMinDenom();
int maxDenom = initialRule->scaleMaxDenom();
QString filter = initialRule->filterExpression();
@ -627,12 +580,11 @@ QgsRuleBasedRendererV2::RuleList QgsRuleBasedRendererV2::refineRuleScales( QgsRu
continue; // jump over the first scales out of the interval
if ( maxDenom != 0 && maxDenom <= scale )
break; // ignore the latter scales out of the interval
rules.append( new Rule( symbol->clone(), oldScale, scale, filter, label, description ) );
initialRule->appendChild( new Rule( symbol->clone(), oldScale, scale, filter, label, description ) );
oldScale = scale;
}
// last rule
rules.append( new Rule( symbol->clone(), oldScale, maxDenom, filter, label, description ) );
return rules;
initialRule->appendChild( new Rule( symbol->clone(), oldScale, maxDenom, filter, label, description ) );
}
QString QgsRuleBasedRendererV2::dump()

View File

@ -119,11 +119,22 @@ class CORE_EXPORT QgsRuleBasedRendererV2 : public QgsFeatureRendererV2
static Rule* create( QDomElement& ruleElem, QgsSymbolV2Map& symbolMap );
RuleList& children() { return mChildren; }
Rule* parent() { return mParent; }
//! add child rule, take ownership, sets this as parent
void appendChild( Rule* rule ) { mChildren.append( rule ); rule->mParent = this; }
//! add child rule, take ownership, sets this as parent
void insertChild( int i, Rule* rule ) { mChildren.insert( i, rule ); rule->mParent = this; }
//! delete child rule
void removeChild( Rule* rule ) { mChildren.removeAll( rule ); delete rule; }
//! take child rule out, set parent as null
void takeChild( Rule* rule ) { mChildren.removeAll( rule ); rule->mParent = NULL; }
protected:
void initFilter();
Rule* mParent; // parent rule (NULL only for root rule)
QgsSymbolV2* mSymbol;
int mScaleMinDenom, mScaleMaxDenom;
QString mFilterExp, mLabel, mDescription;
@ -141,8 +152,10 @@ class CORE_EXPORT QgsRuleBasedRendererV2 : public QgsFeatureRendererV2
static QgsFeatureRendererV2* create( QDomElement& element );
//! Constructor. Adds default rule if the symbol is not null (and takes ownership of it)
QgsRuleBasedRendererV2( QgsSymbolV2* defaultSymbol = NULL );
//! Constructs the renderer from given tree of rules (takes ownership)
QgsRuleBasedRendererV2( QgsRuleBasedRendererV2::Rule* root );
//! Constructor for convenience. Creates a root rule and adds a default rule with symbol (takes ownership)
QgsRuleBasedRendererV2( QgsSymbolV2* defaultSymbol );
~QgsRuleBasedRendererV2();
@ -178,31 +191,14 @@ class CORE_EXPORT QgsRuleBasedRendererV2 : public QgsFeatureRendererV2
Rule* rootRule() { return mRootRule; }
//! return the total number of rules
int ruleCount();
//! get reference to rule at index (valid indexes: 0...count-1)
Rule* ruleAt( int index );
//! add rule to the end of the list of rules. takes ownership
void addRule( Rule* rule );
//! insert rule to a specific position of the list of rules. takes ownership
void insertRule( int index, Rule* rule );
//! modify the rule at a specific position of the list of rules. takes ownership
void updateRuleAt( int index, Rule* rule );
//! remove the rule at the specified index
void removeRuleAt( int index );
//! swap the two rules specified by the indices
void swapRules( int index1, int index2 );
//////
//! take a rule and create a list of new rules based on the categories from categorized symbol renderer
static RuleList refineRuleCategories( Rule* initialRule, QgsCategorizedSymbolRendererV2* r );
static void refineRuleCategories( Rule* initialRule, QgsCategorizedSymbolRendererV2* r );
//! take a rule and create a list of new rules based on the ranges from graduated symbol renderer
static RuleList refineRuleRanges( Rule* initialRule, QgsGraduatedSymbolRendererV2* r );
static void refineRuleRanges( Rule* initialRule, QgsGraduatedSymbolRendererV2* r );
//! take a rule and create a list of new rules with intervals of scales given by the passed scale denominators
static RuleList refineRuleScales( Rule* initialRule, QList<int> scales );
static void refineRuleScales( Rule* initialRule, QList<int> scales );
protected:
//! the root node with hierarchical list of rules

View File

@ -58,7 +58,9 @@ QgsRuleBasedRendererV2Widget::QgsRuleBasedRendererV2Widget( QgsVectorLayer* laye
setupUi( this );
treeRules->setRenderer( mRenderer );
mModel = new QgsRuleBasedRendererV2Model( mRenderer );
viewRules->setModel( mModel );
mRefineMenu = new QMenu( btnRefineRule );
mRefineMenu->addAction( tr( "Add scales" ), this, SLOT( refineRuleScales() ) );
mRefineMenu->addAction( tr( "Add categories" ), this, SLOT( refineRuleCategories() ) );
@ -71,8 +73,10 @@ QgsRuleBasedRendererV2Widget::QgsRuleBasedRendererV2Widget( QgsVectorLayer* laye
btnMoveUp->setIcon( QIcon( QgsApplication::iconPath( "symbologyUp.png" ) ) );
btnMoveDown->setIcon( QIcon( QgsApplication::iconPath( "symbologyDown.png" ) ) );
connect( treeRules, SIGNAL( itemDoubleClicked( QTreeWidgetItem*, int ) ), this, SLOT( editRule() ) );
connect( treeRules, SIGNAL( customContextMenuRequested( const QPoint& ) ), this, SLOT( contextMenuViewCategories( const QPoint& ) ) );
connect( viewRules, SIGNAL( doubleClicked( const QModelIndex & ) ), this, SLOT( editRule( const QModelIndex & ) ) );
// support for context menu (now handled generically)
connect( viewRules, SIGNAL( customContextMenuRequested( const QPoint& ) ), this, SLOT( contextMenuViewCategories( const QPoint& ) ) );
connect( btnAddRule, SIGNAL( clicked() ), this, SLOT( addRule() ) );
connect( btnEditRule, SIGNAL( clicked() ), this, SLOT( editRule() ) );
@ -80,13 +84,7 @@ QgsRuleBasedRendererV2Widget::QgsRuleBasedRendererV2Widget( QgsVectorLayer* laye
connect( btnMoveUp, SIGNAL( clicked() ), this, SLOT( moveUp() ) );
connect( btnMoveDown, SIGNAL( clicked() ), this, SLOT( moveDown() ) );
connect( radNoGrouping, SIGNAL( clicked() ), this, SLOT( setGrouping() ) );
connect( radGroupFilter, SIGNAL( clicked() ), this, SLOT( setGrouping() ) );
connect( radGroupScale, SIGNAL( clicked() ), this, SLOT( setGrouping() ) );
connect( btnRenderingOrder, SIGNAL( clicked() ), this, SLOT( setRenderingOrder() ) );
treeRules->setGrouping( QgsRendererRulesTreeWidget::NoGrouping );
}
QgsRuleBasedRendererV2Widget::~QgsRuleBasedRendererV2Widget()
@ -99,17 +97,6 @@ QgsFeatureRendererV2* QgsRuleBasedRendererV2Widget::renderer()
return mRenderer;
}
void QgsRuleBasedRendererV2Widget::setGrouping()
{
QObject* origin = sender();
if ( origin == radNoGrouping )
treeRules->setGrouping( QgsRendererRulesTreeWidget::NoGrouping );
else if ( origin == radGroupFilter )
treeRules->setGrouping( QgsRendererRulesTreeWidget::GroupingByFilter );
else if ( origin == radGroupScale )
treeRules->setGrouping( QgsRendererRulesTreeWidget::GroupingByScale );
}
void QgsRuleBasedRendererV2Widget::addRule()
{
QgsSymbolV2* s = QgsSymbolV2::defaultSymbol( mLayer->geometryType() );
@ -118,10 +105,21 @@ void QgsRuleBasedRendererV2Widget::addRule()
QgsRendererRulePropsDialog dlg( newrule, mLayer, mStyle );
if ( dlg.exec() )
{
// add rule
dlg.updateRuleFromGui();
mRenderer->addRule( newrule ); // takes ownership
treeRules->populateRules();
QgsRuleBasedRendererV2::Rule* current = currentRule();
if ( current )
{
// add after this rule
QModelIndex currentIndex = viewRules->selectionModel()->currentIndex();
mModel->insertRule( currentIndex.parent(), currentIndex.row() + 1, newrule );
}
else
{
// append to root rule
int rows = mModel->rowCount();
mModel->insertRule( QModelIndex(), rows, newrule );
}
}
else
{
@ -129,110 +127,56 @@ void QgsRuleBasedRendererV2Widget::addRule()
}
}
QgsRuleBasedRendererV2::Rule* QgsRuleBasedRendererV2Widget::currentRule()
{
QItemSelectionModel* sel = viewRules->selectionModel();
QModelIndex idx = sel->currentIndex();
if ( !idx.isValid() )
return NULL;
return static_cast<QgsRuleBasedRendererV2::Rule*>( idx.internalPointer() );
}
void QgsRuleBasedRendererV2Widget::editRule()
{
QTreeWidgetItem * item = treeRules->currentItem();
if ( ! item )
return;
editRule( viewRules->selectionModel()->currentIndex() );
}
int rule_index = item->data( 0, Qt::UserRole + 1 ).toInt();
if ( rule_index < 0 )
{
QMessageBox::information( this, tr( "Edit rule" ), tr( "Groups of rules cannot be edited." ) );
void QgsRuleBasedRendererV2Widget::editRule( const QModelIndex& index )
{
if ( !index.isValid() )
return;
}
QgsRuleBasedRendererV2::Rule* rule = mRenderer->ruleAt( rule_index )->clone();
QgsRuleBasedRendererV2::Rule* rule = static_cast<QgsRuleBasedRendererV2::Rule*>( index.internalPointer() );
QgsRendererRulePropsDialog dlg( rule, mLayer, mStyle );
if ( dlg.exec() )
{
// update rule
dlg.updateRuleFromGui();
mRenderer->updateRuleAt( rule_index, rule );
treeRules->populateRules();
}
else
{
delete rule;
// model should know about the change and emit dataChanged signal for the view
mModel->updateRule( index );
}
}
void QgsRuleBasedRendererV2Widget::removeRule()
{
QTreeWidgetItem * item = treeRules->currentItem();
if ( ! item )
QModelIndex index = viewRules->selectionModel()->currentIndex();
if ( !index.isValid() )
return;
int rule_index = item->data( 0, Qt::UserRole + 1 ).toInt();
if ( rule_index < 0 )
{
QList<int> rule_indexes;
// this is a group
for ( int i = 0; i < item->childCount(); i++ )
{
int idx = item->child( i )->data( 0, Qt::UserRole + 1 ).toInt();
rule_indexes.append( idx );
}
// delete in decreasing order to avoid shifting of indexes
qSort( rule_indexes.begin(), rule_indexes.end(), qGreater<int>() );
foreach( int idx, rule_indexes )
mRenderer->removeRuleAt( idx );
}
else
{
// this is a rule
mRenderer->removeRuleAt( rule_index );
}
treeRules->populateRules();
mModel->removeRule( index );
}
void QgsRuleBasedRendererV2Widget::moveUp()
{
QTreeWidgetItem * item = treeRules->currentItem();
if ( ! item ) return; // No rule selected, exit
int rule_index = item->data( 0, Qt::UserRole + 1 ).toInt();
if ( rule_index < 0 )
{
return;// Group of rules selected, exit
}
else
{
if ( rule_index > 0 ) // do not move up the first rule
{
mRenderer->swapRules( rule_index, rule_index - 1 );
treeRules->populateRules();
// TODO: find out where the moved rule goes and reselect it (at least for non-grouped display)
// maybe based on the following functions :
// findItems(QString(rule_index - 1), Qt::MatchExactly, 4).first.index)
// setCurrentItem, setSelected, scrollToItem
}
}
// TODO: solve directly by drag'n'drop
}
void QgsRuleBasedRendererV2Widget::moveDown()
{
QTreeWidgetItem * item = treeRules->currentItem();
if ( ! item ) return; // No rule selected, exit
int rule_index = item->data( 0, Qt::UserRole + 1 ).toInt();
if ( rule_index < 0 )
{
return;// Group of rules selected, exit
}
else
{
if ( rule_index + 1 < mRenderer->ruleCount() ) // do not move down the last rule
{
mRenderer->swapRules( rule_index, rule_index + 1 );
treeRules->populateRules();
}
}
// TODO: solve directly by drag'n'drop
}
@ -248,38 +192,23 @@ void QgsRuleBasedRendererV2Widget::moveDown()
void QgsRuleBasedRendererV2Widget::refineRule( int type )
{
QTreeWidgetItem * item = treeRules->currentItem();
if ( ! item )
QModelIndex index = viewRules->selectionModel()->currentIndex();
if ( !index.isValid() )
return;
int rule_index = item->data( 0, Qt::UserRole + 1 ).toInt();
if ( rule_index < 0 )
return;
QgsRuleBasedRendererV2::Rule* initialRule = static_cast<QgsRuleBasedRendererV2::Rule*>( index.internalPointer() );
QgsRuleBasedRendererV2::Rule* initialRule = mRenderer->ruleAt( rule_index );
QgsRuleBasedRendererV2::RuleList refinedRules;
if ( type == 0 ) // categories
refinedRules = refineRuleCategoriesGui( initialRule );
refineRuleCategoriesGui( initialRule );
else if ( type == 1 ) // ranges
refinedRules = refineRuleRangesGui( initialRule );
refineRuleRangesGui( initialRule );
else // scales
refinedRules = refineRuleScalesGui( initialRule );
refineRuleScalesGui( initialRule );
if ( refinedRules.count() == 0 )
return;
// TODO: set initial rule's symbol to NULL (?)
// delete original rule
mRenderer->removeRuleAt( rule_index );
// add new rules
for ( int i = 0; i < refinedRules.count(); i++ )
{
mRenderer->insertRule( rule_index + i, refinedRules.at( i ) );
}
treeRules->populateRules();
// TODO: let model know things have changed
mModel->updateRule( index );
}
void QgsRuleBasedRendererV2Widget::refineRuleCategories()
@ -297,7 +226,7 @@ void QgsRuleBasedRendererV2Widget::refineRuleScales()
refineRule( 2 );
}
QgsRuleBasedRendererV2::RuleList QgsRuleBasedRendererV2Widget::refineRuleCategoriesGui( QgsRuleBasedRendererV2::Rule* initialRule )
void QgsRuleBasedRendererV2Widget::refineRuleCategoriesGui( QgsRuleBasedRendererV2::Rule* initialRule )
{
QDialog dlg;
dlg.setWindowTitle( tr( "Refine a rule to categories" ) );
@ -311,15 +240,15 @@ QgsRuleBasedRendererV2::RuleList QgsRuleBasedRendererV2Widget::refineRuleCategor
dlg.setLayout( l );
if ( !dlg.exec() )
return QgsRuleBasedRendererV2::RuleList();
return;
// create new rules
QgsCategorizedSymbolRendererV2* r = static_cast<QgsCategorizedSymbolRendererV2*>( w->renderer() );
return QgsRuleBasedRendererV2::refineRuleCategories( initialRule, r );
QgsRuleBasedRendererV2::refineRuleCategories( initialRule, r );
}
QgsRuleBasedRendererV2::RuleList QgsRuleBasedRendererV2Widget::refineRuleRangesGui( QgsRuleBasedRendererV2::Rule* initialRule )
void QgsRuleBasedRendererV2Widget::refineRuleRangesGui( QgsRuleBasedRendererV2::Rule* initialRule )
{
QDialog dlg;
dlg.setWindowTitle( tr( "Refine a rule to ranges" ) );
@ -333,20 +262,20 @@ QgsRuleBasedRendererV2::RuleList QgsRuleBasedRendererV2Widget::refineRuleRangesG
dlg.setLayout( l );
if ( !dlg.exec() )
return QgsRuleBasedRendererV2::RuleList();
return;
// create new rules
QgsGraduatedSymbolRendererV2* r = static_cast<QgsGraduatedSymbolRendererV2*>( w->renderer() );
return QgsRuleBasedRendererV2::refineRuleRanges( initialRule, r );
QgsRuleBasedRendererV2::refineRuleRanges( initialRule, r );
}
QgsRuleBasedRendererV2::RuleList QgsRuleBasedRendererV2Widget::refineRuleScalesGui( QgsRuleBasedRendererV2::Rule* initialRule )
void QgsRuleBasedRendererV2Widget::refineRuleScalesGui( QgsRuleBasedRendererV2::Rule* initialRule )
{
QString txt = QInputDialog::getText( this,
tr( "Scale refinement" ),
tr( "Please enter scale denominators at which will split the rule, separate them by commas (e.g. 1000,5000):" ) );
if ( txt.isEmpty() )
return QgsRuleBasedRendererV2::RuleList();
return;
QList<int> scales;
bool ok;
@ -359,7 +288,7 @@ QgsRuleBasedRendererV2::RuleList QgsRuleBasedRendererV2Widget::refineRuleScalesG
QMessageBox::information( this, tr( "Error" ), QString( tr( "\"%1\" is not valid scale denominator, ignoring it." ) ).arg( item ) );
}
return QgsRuleBasedRendererV2::refineRuleScales( initialRule, scales );
QgsRuleBasedRendererV2::refineRuleScales( initialRule, scales );
}
QList<QgsSymbolV2*> QgsRuleBasedRendererV2Widget::selectedSymbols()
@ -371,14 +300,16 @@ QList<QgsSymbolV2*> QgsRuleBasedRendererV2Widget::selectedSymbols()
return symbolList;
}
QList<QTreeWidgetItem*> selectedItems = treeRules->selectedItems();
QList<QTreeWidgetItem*>::const_iterator it = selectedItems.constBegin();
for ( ; it != selectedItems.constEnd(); ++it )
QItemSelection sel = viewRules->selectionModel()->selection();
foreach( QItemSelectionRange range, sel )
{
int priority = ( *it )->data( 0, Qt::UserRole + 1 ).toInt();
if ( priority < mRenderer->ruleCount() )
QModelIndex parent = range.parent();
QgsRuleBasedRendererV2::Rule* parentRule = !parent.isValid() ? mRenderer->rootRule() :
static_cast<QgsRuleBasedRendererV2::Rule*>( parent.internalPointer() );
QgsRuleBasedRendererV2::RuleList& children = parentRule->children();
for ( int row = range.top(); row <= range.bottom(); row++ )
{
symbolList.append( mRenderer->ruleAt( priority )->symbol() );
symbolList.append( children[row]->symbol() );
}
}
@ -387,10 +318,13 @@ QList<QgsSymbolV2*> QgsRuleBasedRendererV2Widget::selectedSymbols()
void QgsRuleBasedRendererV2Widget::refreshSymbolView()
{
// TODO: model/view
/*
if ( treeRules )
{
treeRules->populateRules();
}
*/
}
#include "qgssymbollevelsv2dialog.h"
@ -488,254 +422,197 @@ void QgsRendererRulePropsDialog::updateRuleFromGui()
////////
QgsRendererRulesTreeWidget::QgsRendererRulesTreeWidget( QWidget* parent )
: QTreeWidget( parent ), mR( NULL ), mGrouping( NoGrouping )
{
mLongestMinDenom = 0;
mLongestMaxDenom = 0;
/*
setDragEnabled(true);
viewport()->setAcceptDrops(true);
setDropIndicatorShown(true);
setDragDropMode(QAbstractItemView::InternalMove);
*/
setSelectionMode( QAbstractItemView::SingleSelection );
/*
setDragEnabled(true);
viewport()->setAcceptDrops(true);
setDropIndicatorShown(true);
setDragDropMode(QAbstractItemView::InternalMove);
*/
}
void QgsRendererRulesTreeWidget::setRenderer( QgsRuleBasedRendererV2* r )
{
mR = r;
}
void QgsRendererRulesTreeWidget::setGrouping( Grouping g )
{
mGrouping = g;
setRootIsDecorated( true ); //mGrouping != NoGrouping );
populateRules();
}
QString QgsRendererRulesTreeWidget::formatScaleRange( int minDenom, int maxDenom )
{
if ( maxDenom != 0 )
return QString( "<1:%L1, 1:%L2>" ).arg( minDenom ).arg( maxDenom );
else
return QString( "<1:%L1, 1:inf>" ).arg( minDenom );
}
QString QgsRendererRulesTreeWidget::formatScale( int denom, int size )
static QString _formatScale( int denom )
{
if ( denom != 0 )
{
QString txt = QString( "1:%L1" ).arg( denom );
if ( size > 0 )
txt.prepend( QString( size - txt.count(), QChar( ' ' ) ) );
return txt;
}
else
return QString();
}
#include "qgslogger.h"
void QgsRendererRulesTreeWidget::populateRules()
/////
QgsRuleBasedRendererV2Model::QgsRuleBasedRendererV2Model( QgsRuleBasedRendererV2* r )
: mR( r )
{
if ( !mR )
return;
}
clear();
Qt::ItemFlags QgsRuleBasedRendererV2Model::flags( const QModelIndex &index ) const
{
if ( !index.isValid() )
return 0;
mLongestMinDenom = 0;
mLongestMaxDenom = 0;
// find longest scale string for future padding
// TODO: use a custom model and implement custom sorting
for ( int i = 0; i < mR->ruleCount(); ++i )
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
}
QVariant QgsRuleBasedRendererV2Model::data( const QModelIndex &index, int role ) const
{
if ( !index.isValid() )
return QVariant();
QgsRuleBasedRendererV2::Rule* rule = static_cast<QgsRuleBasedRendererV2::Rule*>( index.internalPointer() );
if ( role == Qt::DisplayRole )
{
QgsRuleBasedRendererV2::Rule* rule = mR->ruleAt( i );
mLongestMinDenom = qMax( mLongestMinDenom, formatScale( rule->scaleMinDenom() ).size() );
mLongestMaxDenom = qMax( mLongestMaxDenom, formatScale( rule->scaleMaxDenom() ).size() );
switch ( index.column() )
{
case 0: return rule->label();
case 1: return rule->filterExpression().isEmpty() ? tr( "(no filter)" ) : rule->filterExpression();
case 2: return rule->dependsOnScale() ? _formatScale( rule->scaleMinDenom() ) : QVariant();
case 3: return rule->dependsOnScale() ? _formatScale( rule->scaleMaxDenom() ) : QVariant();
default: return QVariant();
}
}
else if ( role == Qt::DecorationRole && index.column() == 0 && rule->symbol() )
{
return QgsSymbolLayerV2Utils::symbolPreviewIcon( rule->symbol(), QSize( 16, 16 ) );
}
else if ( role == Qt::TextAlignmentRole )
{
return ( index.column() == 2 || index.column() == 3 ) ? Qt::AlignRight : Qt::AlignLeft;
}
else if ( role == Qt::EditRole )
{
switch ( index.column() )
{
case 0: return rule->label();
case 1: return rule->filterExpression();
case 2: return rule->scaleMinDenom();
case 3: return rule->scaleMaxDenom();
default: return QVariant();
}
}
if ( mGrouping == NoGrouping )
populateRulesNoGrouping();
else if ( mGrouping == GroupingByScale )
populateRulesGroupByScale();
else
populateRulesGroupByFilter();
setColumnWidth( 1, 200 ); // make the column for filter a bit bigger
return QVariant();
}
QTreeWidgetItem* QgsRendererRulesTreeWidget::populateRulesNoGrouping( QgsRuleBasedRendererV2::Rule* rule, int i, QTreeWidgetItem* parentItem )
QVariant QgsRuleBasedRendererV2Model::headerData( int section, Qt::Orientation orientation, int role ) const
{
QTreeWidgetItem* item = new QTreeWidgetItem( parentItem );
QString txtLabel = rule->label();
item->setText( 0, txtLabel );
item->setData( 0, Qt::UserRole + 1, i );
if ( rule->symbol() )
if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 5 )
{
item->setIcon( 0, QgsSymbolLayerV2Utils::symbolPreviewIcon( rule->symbol(), QSize( 16, 16 ) ) );
QStringList lst; lst << tr( "Label" ) << tr( "Rule" ) << tr( "Min. scale" ) << tr( "Max.scale" );
return lst[section];
}
QString txtRule = rule->filterExpression();
if ( txtRule.isEmpty() )
txtRule = tr( "(no filter)" );
item->setText( 1, txtRule );
if ( rule->dependsOnScale() )
{
item->setText( 2, formatScale( rule->scaleMinDenom(), mLongestMinDenom ) );
item->setText( 3, formatScale( rule->scaleMaxDenom(), mLongestMaxDenom ) );
item->setTextAlignment( 2, Qt::AlignRight );
item->setTextAlignment( 3, Qt::AlignRight );
}
// process children
QgsRuleBasedRendererV2::RuleList& children = rule->children();
for ( int i = 0; i < children.count(); ++i )
{
populateRulesNoGrouping( children[i], i, item );
}
return item;
return QVariant();
}
void QgsRendererRulesTreeWidget::populateRulesNoGrouping()
int QgsRuleBasedRendererV2Model::rowCount( const QModelIndex &parent ) const
{
QList<QTreeWidgetItem *> lst;
QgsRuleBasedRendererV2::Rule* parentRule;
if ( parent.column() > 0 )
return 0;
QgsRuleBasedRendererV2::Rule* rule = mR->rootRule();
QTreeWidgetItem* item = populateRulesNoGrouping( rule, -1, NULL );
if ( !parent.isValid() )
parentRule = mR->rootRule();
else
parentRule = static_cast<QgsRuleBasedRendererV2::Rule*>( parent.internalPointer() );
lst << item;
addTopLevelItems( lst );
return parentRule->children().count();
}
void QgsRendererRulesTreeWidget::populateRulesGroupByScale()
int QgsRuleBasedRendererV2Model::columnCount( const QModelIndex & ) const
{
QMap< QPair<int, int>, QTreeWidgetItem*> scale_items;
QFont italicFont;
italicFont.setItalic( true );
for ( int i = 0; i < mR->ruleCount(); ++i )
{
QgsRuleBasedRendererV2::Rule* rule = mR->ruleAt( i );
QPair<int, int> scale = qMakePair( rule->scaleMinDenom(), rule->scaleMaxDenom() );
if ( ! scale_items.contains( scale ) )
{
QString txt;
if ( rule->dependsOnScale() )
txt = tr( "scale " ) + formatScaleRange( rule->scaleMinDenom(), rule->scaleMaxDenom() );
else
txt = tr( "any scale" );
QTreeWidgetItem* scale_item = new QTreeWidgetItem;
scale_item->setText( 0, txt );
scale_item->setData( 0, Qt::UserRole + 1, -2 );
scale_item->setFlags( scale_item->flags() & ~Qt::ItemIsDragEnabled ); // groups cannot be dragged
scale_item->setFont( 0, italicFont );
scale_items[scale] = scale_item;
// need to add the item before setFirstColumnSpanned,
// see http://qt.nokia.com/developer/task-tracker/index_html?method=entry&id=214686
addTopLevelItem( scale_item );
scale_item->setFirstColumnSpanned( true );
}
QString filter = rule->filterExpression();
QTreeWidgetItem* item = new QTreeWidgetItem( scale_items[scale] );
QString txtLabel = rule->label();
item->setText( 0, txtLabel );
item->setData( 0, Qt::UserRole + 1, i );
item->setIcon( 0, QgsSymbolLayerV2Utils::symbolPreviewIcon( rule->symbol(), QSize( 16, 16 ) ) );
QString txtRule = rule->filterExpression();
if ( txtRule.isEmpty() )
txtRule = tr( "(no filter)" );
item->setText( 1, txtRule );
if ( rule->dependsOnScale() )
{
// Displaying scales is redundant here, but keeping them allows to keep constant the layout and width of all columns when switching to one of the two other views
item->setText( 2, formatScale( rule->scaleMinDenom(), mLongestMinDenom ) );
item->setText( 3, formatScale( rule->scaleMaxDenom(), mLongestMaxDenom ) );
item->setTextAlignment( 2, Qt::AlignRight );
item->setTextAlignment( 3, Qt::AlignRight );
}
//item->setBackground( 1, Qt::lightGray );
//item->setBackground( 3, Qt::lightGray );
// Priority (Id): add 1 to rule number and convert to string
item->setText( 4, QString( "%1" ).arg( i + 1, 4 ) );
item->setTextAlignment( 4, Qt::AlignRight );
}
addTopLevelItems( scale_items.values() );
return 4;
}
void QgsRendererRulesTreeWidget::populateRulesGroupByFilter()
QModelIndex QgsRuleBasedRendererV2Model::index( int row, int column, const QModelIndex &parent ) const
{
QMap<QString, QTreeWidgetItem *> filter_items;
if ( !hasIndex( row, column, parent ) )
return QModelIndex();
QFont italicFont;
italicFont.setItalic( true );
QgsRuleBasedRendererV2::Rule* parentRule;
for ( int i = 0; i < mR->ruleCount(); ++i )
if ( !parent.isValid() )
parentRule = mR->rootRule();
else
parentRule = static_cast<QgsRuleBasedRendererV2::Rule*>( parent.internalPointer() );
QgsRuleBasedRendererV2::Rule* childRule = parentRule->children()[row];
if ( childRule )
return createIndex( row, column, childRule );
else
return QModelIndex();
}
QModelIndex QgsRuleBasedRendererV2Model::parent( const QModelIndex &index ) const
{
if ( !index.isValid() )
return QModelIndex();
QgsRuleBasedRendererV2::Rule* childRule = static_cast<QgsRuleBasedRendererV2::Rule*>( index.internalPointer() );
QgsRuleBasedRendererV2::Rule* parentRule = childRule->parent();
if ( parentRule == mR->rootRule() )
return QModelIndex();
int row = parentRule->children().indexOf( childRule );
return createIndex( row, 0, parentRule );
}
bool QgsRuleBasedRendererV2Model::setData( const QModelIndex & index, const QVariant & value, int role )
{
if ( !index.isValid() || role != Qt::EditRole )
return false;
QgsRuleBasedRendererV2::Rule* rule = static_cast<QgsRuleBasedRendererV2::Rule*>( index.internalPointer() );
switch ( index.column() )
{
QgsRuleBasedRendererV2::Rule* rule = mR->ruleAt( i );
QString filter = rule->filterExpression();
if ( ! filter_items.contains( filter ) )
{
QTreeWidgetItem* filter_item = new QTreeWidgetItem;
filter_item->setText( 0, filter.isEmpty() ? tr( "(no filter)" ) : filter );
filter_item->setData( 0, Qt::UserRole + 1, -1 );
filter_item->setFlags( filter_item->flags() & ~Qt::ItemIsDragEnabled ); // groups cannot be dragged
filter_item->setFont( 0, italicFont );
filter_items[filter] = filter_item;
// need to add the item before setFirstColumnSpanned,
// see http://qt.nokia.com/developer/task-tracker/index_html?method=entry&id=214686
addTopLevelItem( filter_item );
filter_item->setFirstColumnSpanned( true );
}
QTreeWidgetItem* item = new QTreeWidgetItem( filter_items[filter] );
QString txtLabel = rule->label();
item->setText( 0, txtLabel );
item->setData( 0, Qt::UserRole + 1, i );
item->setIcon( 0, QgsSymbolLayerV2Utils::symbolPreviewIcon( rule->symbol(), QSize( 16, 16 ) ) );
// Displaying filter is redundant here, but keeping it allows to keep constant the layout and width of all columns when switching to one of the two other views
item->setText( 1, filter );
// Makes table layout slightly more readable when filters are long strings
//item->setBackground( 1, Qt::lightGray );
//item->setBackground( 3, Qt::lightGray );
if ( rule->dependsOnScale() )
{
item->setText( 2, formatScale( rule->scaleMinDenom(), mLongestMinDenom ) );
item->setText( 3, formatScale( rule->scaleMaxDenom(), mLongestMaxDenom ) );
item->setTextAlignment( 2, Qt::AlignRight );
item->setTextAlignment( 3, Qt::AlignRight );
}
// Priority (Id): add 1 to rule number and convert to string
item->setText( 4, QString( "%1" ).arg( i + 1, 4 ) );
item->setTextAlignment( 4, Qt::AlignRight );
case 0: // label
rule->setLabel( value.toString() );
break;
case 1: // filter
rule->setFilterExpression( value.toString() );
break;
case 2: // scale min
rule->setScaleMinDenom( value.toInt() );
break;
case 3: // scale max
rule->setScaleMaxDenom( value.toInt() );
break;
default:
return false;
}
addTopLevelItems( filter_items.values() );
emit dataChanged( index, index );
return true;
}
void QgsRuleBasedRendererV2Model::insertRule( const QModelIndex& parent, int before, QgsRuleBasedRendererV2::Rule* newrule )
{
beginInsertRows( parent, before, before );
QgsRuleBasedRendererV2::Rule* parentRule = parent.isValid() ?
static_cast<QgsRuleBasedRendererV2::Rule*>( parent.internalPointer() ) : mR->rootRule();
parentRule->insertChild( before, newrule );
endInsertRows();
}
void QgsRuleBasedRendererV2Model::updateRule( const QModelIndex& index )
{
emit dataChanged( index, index );
}
void QgsRuleBasedRendererV2Model::removeRule( const QModelIndex& index )
{
beginRemoveRows( index.parent(), index.row(), index.row() );
QgsRuleBasedRendererV2::Rule* rule = static_cast<QgsRuleBasedRendererV2::Rule*>( index.internalPointer() );
rule->parent()->removeChild( rule );
endRemoveRows();
}

View File

@ -23,41 +23,44 @@ class QMenu;
///////
#include <QTreeWidget>
#include <QAbstractItemModel>
class GUI_EXPORT QgsRendererRulesTreeWidget : public QTreeWidget
/*
Tree model for the rules:
(invalid) == root node
+--- top level rule
+--- top level rule
*/
class QgsRuleBasedRendererV2Model : public QAbstractItemModel
{
Q_OBJECT
public:
QgsRendererRulesTreeWidget( QWidget* parent = 0 );
QgsRuleBasedRendererV2Model( QgsRuleBasedRendererV2* r );
void setRenderer( QgsRuleBasedRendererV2* r );
virtual Qt::ItemFlags flags( const QModelIndex &index ) const;
virtual QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const;
virtual QVariant headerData( int section, Qt::Orientation orientation,
int role = Qt::DisplayRole ) const;
virtual int rowCount( const QModelIndex &parent = QModelIndex() ) const;
virtual int columnCount( const QModelIndex & = QModelIndex() ) const;
//! provide model index for parent's child item
virtual QModelIndex index( int row, int column, const QModelIndex &parent = QModelIndex() ) const;
//! provide parent model index
virtual QModelIndex parent( const QModelIndex &index ) const;
enum Grouping { NoGrouping, GroupingByScale, GroupingByFilter };
virtual bool setData( const QModelIndex & index, const QVariant & value, int role = Qt::EditRole );
void setGrouping( Grouping g );
// new methods
void populateRules();
void insertRule( const QModelIndex& parent, int before, QgsRuleBasedRendererV2::Rule* newrule );
void updateRule( const QModelIndex& index );
void removeRule( const QModelIndex& index );
protected:
void populateRulesNoGrouping();
void populateRulesGroupByScale();
void populateRulesGroupByFilter();
QTreeWidgetItem* populateRulesNoGrouping( QgsRuleBasedRendererV2::Rule* rule, int i, QTreeWidgetItem* parentItem );
QString formatScaleRange( int minDenom, int maxDenom );
QString formatScale( int denom, int size = 0 );
QgsRuleBasedRendererV2* mR;
Grouping mGrouping;
int mLongestMinDenom;
int mLongestMaxDenom;
};
///////
#include "ui_qgsrulebasedrendererv2widget.h"
@ -79,12 +82,11 @@ class GUI_EXPORT QgsRuleBasedRendererV2Widget : public QgsRendererV2Widget, priv
void addRule();
void editRule();
void editRule( const QModelIndex& index );
void removeRule();
void moveUp();
void moveDown();
void setGrouping();
void refineRuleScales();
void refineRuleCategories();
void refineRuleRanges();
@ -94,14 +96,17 @@ class GUI_EXPORT QgsRuleBasedRendererV2Widget : public QgsRendererV2Widget, priv
protected:
void refineRule( int type );
QgsRuleBasedRendererV2::RuleList refineRuleCategoriesGui( QgsRuleBasedRendererV2::Rule* initialRule );
QgsRuleBasedRendererV2::RuleList refineRuleRangesGui( QgsRuleBasedRendererV2::Rule* initialRule );
QgsRuleBasedRendererV2::RuleList refineRuleScalesGui( QgsRuleBasedRendererV2::Rule* initialRule );
void refineRuleCategoriesGui( QgsRuleBasedRendererV2::Rule* initialRule );
void refineRuleRangesGui( QgsRuleBasedRendererV2::Rule* initialRule );
void refineRuleScalesGui( QgsRuleBasedRendererV2::Rule* initialRule );
QgsRuleBasedRendererV2::Rule* currentRule();
QList<QgsSymbolV2*> selectedSymbols();
void refreshSymbolView();
QgsRuleBasedRendererV2* mRenderer;
QgsRuleBasedRendererV2Model* mModel;
QMenu* mRefineMenu;
};

View File

@ -11,42 +11,6 @@
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QgsRendererRulesTreeWidget" name="treeRules">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="headerHidden">
<bool>false</bool>
</property>
<column>
<property name="text">
<string>Label</string>
</property>
</column>
<column>
<property name="text">
<string>Rule</string>
</property>
</column>
<column>
<property name="text">
<string>Min. scale</string>
</property>
</column>
<column>
<property name="text">
<string>Max. scale</string>
</property>
</column>
</widget>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
@ -99,37 +63,6 @@
<string notr="true"/>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Rule grouping</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radNoGrouping">
<property name="text">
<string comment="No grouping for displaying rules">None</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radGroupFilter">
<property name="text">
<string comment="Group rules by filter">By filter</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radGroupScale">
<property name="text">
<string comment="Group rules by scale">By scale</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnRenderingOrder">
<property name="text">
@ -140,15 +73,18 @@
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QTreeView" name="viewRules">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QgsRendererRulesTreeWidget</class>
<extends>QTreeWidget</extends>
<header>qgsrulebasedrendererv2widget.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -84,4 +84,5 @@ ADD_QGIS_TEST(coordinatereferencesystemtest testqgscoordinatereferencesystem.cpp
ADD_QGIS_TEST(pointtest testqgspoint.cpp)
ADD_QGIS_TEST(searchstringtest testqgssearchstring.cpp)
ADD_QGIS_TEST(vectorlayertest testqgsvectorlayer.cpp)
ADD_QGIS_TEST(rulebasedrenderertest testqgsrulebasedrenderer.cpp)

View File

@ -0,0 +1,96 @@
/***************************************************************************
test_template.cpp
--------------------------------------
Date : Sun Sep 16 12:22:23 AKDT 2007
Copyright : (C) 2007 by Gary E. Sherman
Email : sherman at mrcc 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 <QtTest>
#include <QDomDocument>
#include <QFile>
//header for class being tested
#include <qgsrulebasedrendererv2.h>
#if QT_VERSION < 0x40701
// See http://hub.qgis.org/issues/4284
Q_DECLARE_METATYPE( QVariant )
#endif
class TestQgsRuleBasedRenderer: public QObject
{
Q_OBJECT
private slots:
void test_load_xml()
{
QDomDocument doc;
xml2domElement( "rulebasedrenderer_simple.xml", doc );
QDomElement elem = doc.documentElement();
QgsRuleBasedRendererV2* r = static_cast<QgsRuleBasedRendererV2*>( QgsRuleBasedRendererV2::create( elem ) );
QVERIFY( r );
check_tree_valid( r->rootRule() );
delete r;
}
void test_load_invalid_xml()
{
QDomDocument doc;
xml2domElement( "rulebasedrenderer_invalid.xml", doc );
QDomElement elem = doc.documentElement();
QgsRuleBasedRendererV2* r = static_cast<QgsRuleBasedRendererV2*>( QgsRuleBasedRendererV2::create( elem ) );
QVERIFY( r == NULL );
}
private:
void xml2domElement( QString testFile, QDomDocument& doc )
{
QString fileName = QString( TEST_DATA_DIR ) + QDir::separator() + testFile;
QFile f(fileName);
bool fileOpen = f.open(QIODevice::ReadOnly);
QVERIFY( fileOpen );
QString msg;
int line, col;
bool parse = doc.setContent( &f, &msg, &line, &col );
QVERIFY( parse );
}
void check_tree_valid( QgsRuleBasedRendererV2::Rule* root )
{
// root must always exist (although it does not have children)
QVERIFY( root );
// and does not have a parent
QVERIFY( root->parent() == NULL );
foreach ( QgsRuleBasedRendererV2::Rule* node, root->children() )
check_non_root_rule( node );
}
void check_non_root_rule( QgsRuleBasedRendererV2::Rule* node )
{
qDebug() << node->dump();
// children must not be NULL
QVERIFY( node );
// and must have a parent
QVERIFY( node->parent() );
// check that all children are okay
foreach ( QgsRuleBasedRendererV2::Rule* child, node->children() )
check_non_root_rule( child );
}
};
QTEST_MAIN( TestQgsRuleBasedRenderer )
#include "moc_testqgsrulebasedrenderer.cxx"

View File

@ -7,10 +7,13 @@ SET (util_SRCS ../core/qgsrenderchecker.cpp)
# the UI file won't be wrapped!
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_BINARY_DIR}/../../../src/ui
${CMAKE_CURRENT_SOURCE_DIR}/../core #for render checker class
${CMAKE_CURRENT_SOURCE_DIR}/../../../src/gui
${CMAKE_CURRENT_SOURCE_DIR}/../../../src/gui/symbology-ng
${CMAKE_CURRENT_SOURCE_DIR}/../../../src/core
${CMAKE_CURRENT_SOURCE_DIR}/../../../src/core/raster
${CMAKE_CURRENT_SOURCE_DIR}/../../../src/core/symbology-ng
${QT_INCLUDE_DIR}
${GDAL_INCLUDE_DIR}
${PROJ_INCLUDE_DIR}
@ -73,4 +76,23 @@ ENDIF (APPLE)
#ENDIF (APPLE)
# a simple app for testing GUI of renderers
SET(rendererv2gui_SRCS testrendererv2gui.cpp)
SET(rendererv2gui_HDRS testrendererv2gui.h)
QT4_WRAP_CPP(rendererv2gui_MOC_SRCS ${rendererv2gui_HDRS})
ADD_EXECUTABLE(qgis_rendererv2gui ${rendererv2gui_SRCS} ${rendererv2gui_MOC_SRCS})
TARGET_LINK_LIBRARIES(qgis_rendererv2gui
qgis_core
qgis_gui
${QT_QTCORE_LIBRARY}
${QT_QTNETWORK_LIBRARY}
${QT_QTSVG_LIBRARY}
${QT_QTXML_LIBRARY}
${QT_QTWEBKIT_LIBRARY}
${QT_QTMAIN_LIBRARY}
#${QT_QTTEST_LIBRARY}
)

View File

@ -0,0 +1,84 @@
#include "testrendererv2gui.h"
#include <qgsapplication.h>
#include <qgsmapcanvas.h>
#include <qgsvectorlayer.h>
#include <qgsmaplayerregistry.h>
#include <qgsproject.h>
#include <qgsrendererv2propertiesdialog.h>
#include <qgsstylev2.h>
#include <QApplication>
#include <QToolBar>
TestRendererV2GUI::TestRendererV2GUI(QWidget *parent) :
QMainWindow(parent)
{
resize(640,480);
QToolBar* toolBar = addToolBar("Actions");
toolBar->addAction( "set renderer", this, SLOT(setRenderer()) );
mMapCanvas = new QgsMapCanvas(this);
mMapCanvas->setCanvasColor( Qt::white );
setCentralWidget(mMapCanvas);
connect( QgsProject::instance(), SIGNAL(readProject(QDomDocument)), mMapCanvas, SLOT(readProject(QDomDocument)));
}
void TestRendererV2GUI::loadLayers()
{
// load just first vector layer
QList<QgsMapCanvasLayer> canvasLayers;
foreach (QgsMapLayer* layer, QgsMapLayerRegistry::instance()->mapLayers().values())
{
if ( layer->type() == QgsMapLayer::VectorLayer )
canvasLayers << QgsMapCanvasLayer( layer );
}
mMapCanvas->setLayerSet(canvasLayers);
}
void TestRendererV2GUI::setRenderer()
{
QgsMapLayer* layer = mMapCanvas->layer(0);
Q_ASSERT( layer );
Q_ASSERT( layer->type() == QgsMapLayer::VectorLayer );
QgsVectorLayer* vlayer = static_cast<QgsVectorLayer*>(layer);
QgsRendererV2PropertiesDialog dlg( vlayer, QgsStyleV2::defaultStyle() );
dlg.exec();
mMapCanvas->refresh();
}
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
if ( argc < 2 )
{
qDebug( "Provide a project file name with at least one vector layer!" );
return 1;
}
QgsApplication::init();
QgsApplication::initQgis();
TestRendererV2GUI gui;
QString projectFileName( argv[1] );
QgsProject::instance()->setFileName( projectFileName );
bool res = QgsProject::instance()->read();
if ( !res )
{
qDebug("Failed to open project!");
return 1;
}
// the layers are in the registry - now load them!
gui.loadLayers();
gui.show();
return app.exec();
}

View File

@ -0,0 +1,24 @@
#ifndef TESTRENDERERV2GUI_H
#define TESTRENDERERV2GUI_H
#include <QMainWindow>
class QgsMapCanvas;
class TestRendererV2GUI : public QMainWindow
{
Q_OBJECT
public:
explicit TestRendererV2GUI(QWidget *parent = 0);
void loadLayers();
signals:
public slots:
void setRenderer();
protected:
QgsMapCanvas* mMapCanvas;
};
#endif // TESTRENDERERV2GUI_H

View File

@ -0,0 +1,2 @@
<renderer-v2 symbollevels="0" type="RuleRenderer">
</renderer-v2>

View File

@ -0,0 +1,32 @@
<renderer-v2 symbollevels="0" type="RuleRenderer">
<rules>
<rule filter="type=3" symbol="0" label="type3"/>
<rule filter="type=1" symbol="1" label="type1"/>
</rules>
<symbols>
<symbol outputUnit="MM" alpha="1" type="line" name="0">
<layer pass="0" class="SimpleLine" locked="0">
<prop k="capstyle" v="square"/>
<prop k="color" v="220,0,0,255"/>
<prop k="customdash" v="5;2"/>
<prop k="joinstyle" v="bevel"/>
<prop k="offset" v="0"/>
<prop k="penstyle" v="solid"/>
<prop k="use_custom_dash" v="0"/>
<prop k="width" v="0.26"/>
</layer>
</symbol>
<symbol outputUnit="MM" alpha="1" type="line" name="1">
<layer pass="0" class="SimpleLine" locked="0">
<prop k="capstyle" v="square"/>
<prop k="color" v="94,116,254,255"/>
<prop k="customdash" v="5;2"/>
<prop k="joinstyle" v="bevel"/>
<prop k="offset" v="0"/>
<prop k="penstyle" v="solid"/>
<prop k="use_custom_dash" v="0"/>
<prop k="width" v="0.26"/>
</layer>
</symbol>
</symbols>
</renderer-v2>