535 changes: 206 additions & 329 deletions src/gui/symbology-ng/qgsrulebasedrendererv2widget.cpp

Large diffs are not rendered by default.

61 changes: 33 additions & 28 deletions src/gui/symbology-ng/qgsrulebasedrendererv2widget.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,41 +23,44 @@ class QMenu;

///////

#include <QTreeWidget>
#include <QAbstractItemModel>

class GUI_EXPORT QgsRendererRulesTreeWidget : public QTreeWidget
{
Q_OBJECT
/*
Tree model for the rules:
(invalid) == root node
+--- top level rule
+--- top level rule
*/
class QgsRuleBasedRendererV2Model : public QAbstractItemModel
{
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"
Expand All @@ -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();
Expand All @@ -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;
};
Expand Down
84 changes: 10 additions & 74 deletions src/ui/qgsrulebasedrendererv2widget.ui
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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">
Expand All @@ -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>
1 change: 1 addition & 0 deletions tests/src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

96 changes: 96 additions & 0 deletions tests/src/core/testqgsrulebasedrenderer.cpp
Original file line number Diff line number Diff line change
@@ -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"

22 changes: 22 additions & 0 deletions tests/src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down Expand Up @@ -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}
)
84 changes: 84 additions & 0 deletions tests/src/gui/testrendererv2gui.cpp
Original file line number Diff line number Diff line change
@@ -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();
}
24 changes: 24 additions & 0 deletions tests/src/gui/testrendererv2gui.h
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions tests/testdata/rulebasedrenderer_invalid.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<renderer-v2 symbollevels="0" type="RuleRenderer">
</renderer-v2>
32 changes: 32 additions & 0 deletions tests/testdata/rulebasedrenderer_simple.xml
Original file line number Diff line number Diff line change
@@ -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>