In progress support for retrieval of html composer and items by id from map composition. Also refactoring python composer tests.

This commit is contained in:
Tim Sutton 2012-09-19 17:15:39 +02:00
parent 014175e534
commit 11dead9a3a
7 changed files with 206 additions and 81 deletions

View File

@ -42,6 +42,10 @@ public:
/**Removes and deletes all frames from mComposition*/
void deleteFrames();
int nFrames() const;
int nFrames() const /Deprecated/;
/** Return the number of frames associated with this multiframeset.
@note added in 2.0, replaces nFrames
**/
int frameCount() const;
QgsComposerFrame* frame( int i );
};

View File

@ -238,7 +238,7 @@ void QgsComposerMultiFrame::deleteFrames()
mResizeMode = bkResizeMode;
}
QgsComposerFrame* QgsComposerMultiFrame::frame( int i )
QgsComposerFrame* QgsComposerMultiFrame::frame( int i ) const
{
if ( i >= mFrameItems.size() )
{

View File

@ -69,8 +69,14 @@ class CORE_EXPORT QgsComposerMultiFrame: public QObject
/**Removes and deletes all frames from mComposition*/
void deleteFrames();
int nFrames() const { return mFrameItems.size(); }
QgsComposerFrame* frame( int i );
/** Deprecated in 2.0 use frameCount rather. **/
Q_DECL_DEPRECATED int nFrames() const { return mFrameItems.size(); }
/** Return the number of frames associated with this multiframeset.
@note added in 2.0, replaces nFrames
**/
int frameCount() const { return mFrameItems.size(); }
QgsComposerFrame* frame( int i ) const;
protected:
QgsComposition* mComposition;

View File

@ -185,8 +185,6 @@ QList<const QgsComposerMap*> QgsComposition::composerMapItems() const
const QgsComposerMap* QgsComposition::getComposerMapById( int id ) const
{
QList<const QgsComposerMap*> resultList;
QList<QGraphicsItem *> itemList = items();
QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
for ( ; itemIt != itemList.end(); ++itemIt )
@ -200,7 +198,47 @@ const QgsComposerMap* QgsComposition::getComposerMapById( int id ) const
}
}
}
return 0;
}
const QgsComposerHtml* QgsComposition::getComposerHtmlByItem( QgsComposerItem *item ) const
{
QList<QGraphicsItem *> itemList = items();
QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
for ( ; itemIt != itemList.end(); ++itemIt )
{
const QgsComposerHtml* composerHtml = dynamic_cast<const QgsComposerHtml *>( *itemIt );
if ( composerHtml )
{
//Now cycle through the items associated with this html composer
//and return the composer if the item matches any of them
for ( int i=0; i<composerHtml->frameCount(); i++ )
{
if ( composerHtml->frame(i)->id() == item->id() )
{
return composerHtml;
}
}
}
}
return 0;
}
const QgsComposerItem* QgsComposition::getComposerItemById( QString theId ) const
{
QList<QGraphicsItem *> itemList = items();
QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
for ( ; itemIt != itemList.end(); ++itemIt )
{
const QgsComposerItem* mypItem = dynamic_cast<const QgsComposerItem *>( *itemIt );
if ( mypItem )
{
if ( mypItem->id() == theId )
{
return mypItem;
}
}
}
return 0;
}
@ -1282,7 +1320,7 @@ void QgsComposition::removeComposerItem( QgsComposerItem* item, bool createComma
//check if there are frames left. If not, remove the multi frame
if ( frameItem && multiFrame )
{
if ( multiFrame->nFrames() < 1 )
if ( multiFrame->frameCount() < 1 )
{
removeMultiFrame( multiFrame );
if ( createCommand )

View File

@ -124,9 +124,25 @@ class CORE_EXPORT QgsComposition: public QGraphicsScene
template<class T> void composerItems( QList<T*>& itemList );
/**Returns the composer map with specified id
@return id or 0 pointer if the composer map item does not exist*/
@return QgsComposerMap or 0 pointer if the composer map item does not exist*/
const QgsComposerMap* getComposerMapById( int id ) const;
/*Returns the composer html with specified id (a string as named in the
composer user interface item properties).
@note Added in QGIS 2.0
@param id - A QString representing the id of the item.
@return QgsComposerHtml pointer or 0 pointer if no such item exists.
*/
const QgsComposerHtml* getComposerHtmlByItem( QgsComposerItem *item ) const;
/**Returns a composer item given its text identifier.
@note added in 2.0
@param theId - A QString representing the identifier of the item to
retrieve.
@return QgsComposerItem pointer or 0 pointer if no such item exists.
**/
const QgsComposerItem* getComposerItemById( QString theId ) const;
int printResolution() const {return mPrintResolution;}
void setPrintResolution( int dpi ) {mPrintResolution = dpi;}

View File

@ -14,7 +14,7 @@ qgscompositionchecker.py - check rendering of Qgscomposition against an expected
* *
***************************************************************************/
'''
from PyQt4.QtCore import *
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qgis.core import *
@ -22,17 +22,18 @@ class QgsCompositionChecker:
def testComposition(self, mTestName, mComposition, mExpectedImageFile, page=0 ):
if ( mComposition == None):
return false
myMessage = "Composition not valid"
return False, myMessage
#load expected image
expectedImage = QImage( mExpectedImageFile )
#get width/height, create image and render the composition to it
width = expectedImage.width();
height = expectedImage.height();
outputImage = QImage( QSize( width, height ), QImage.Format_ARGB32 )
mComposition.setPlotStyle( QgsComposition.Print )
outputImage.setDotsPerMeterX( expectedImage.dotsPerMeterX() )
outputImage.setDotsPerMeterY( expectedImage.dotsPerMeterX() )
@ -40,29 +41,47 @@ class QgsCompositionChecker:
p = QPainter( outputImage )
mComposition.renderPage( p, page )
p.end()
renderedFilePath = QDir.tempPath() + QDir.separator() + QFileInfo( mExpectedImageFile ).baseName() + "_rendered_python.png"
outputImage.save( renderedFilePath, "PNG" )
diffFilePath = QDir.tempPath() + QDir.separator() + QFileInfo( mExpectedImageFile ).baseName() + "_diff_python.png"
testResult = self.compareImages( expectedImage, outputImage, diffFilePath )
myDashMessage = "<DartMeasurementFile name=\"Rendered Image " + mTestName + "\"" + " type=\"image/png\">" + renderedFilePath + "</DartMeasurementFile>" + "\n" + "<DartMeasurementFile name=\"Expected Image " + mTestName + "\"" + " type=\"image/png\">" + mExpectedImageFile + "</DartMeasurementFile>" + "\n" + "<DartMeasurementFile name=\"Difference Image " + mTestName + "\"" + " type=\"image/png\">" + diffFilePath + "</DartMeasurementFile>"
myDashMessage = (('<DartMeasurementFile name="Rendered Image '
'%s" type="image/png">'
'%s</DartMeasurementFile>'
'<DartMeasurementFile name="Expected Image '
'%s" type="image/png">'
'%s</DartMeasurementFile>\n'
'<DartMeasurementFile name="Difference Image '
'%s" type="image/png">'
'%s</DartMeasurementFile>') %
(mTestName, renderedFilePath, mTestName,
mExpectedImageFile, mTestName, diffFilePath )
)
qDebug( myDashMessage )
return testResult
if not testResult:
myMessage = ('Expected: %s\nGot: %s\nDifference: %s\n' %
(mExpectedImageFile, renderedFilePath, diffFilePath))
else:
myMessage = 'Control and test images matched.'
return testResult, myMessage
def compareImages( self, imgExpected, imgRendered, differenceImagePath ):
if ( imgExpected.width() != imgRendered.width() or imgExpected.height() != imgRendered.height() ):
return false
if ( imgExpected.width() != imgRendered.width()
or imgExpected.height() != imgRendered.height() ):
return False
imageWidth = imgExpected.width()
imageHeight = imgExpected.height()
mismatchCount = 0
differenceImage = QImage( imageWidth, imageHeight, QImage.Format_ARGB32_Premultiplied )
differenceImage = QImage(
imageWidth, imageHeight, QImage.Format_ARGB32_Premultiplied )
differenceImage.fill( qRgb( 152, 219, 249 ) )
pixel1 = QColor().rgb()
pixel2 = QColor().rgb()
for i in range( imageHeight ):
@ -72,13 +91,12 @@ class QgsCompositionChecker:
if ( pixel1 != pixel2 ):
mismatchCount = mismatchCount + 1
differenceImage.setPixel( j, i, qRgb( 255, 0, 0 ) )
if not differenceImagePath.isEmpty():
differenceImage.save( differenceImagePath, "PNG" )
#allow pixel deviation of 1 percent
pixelCount = imageWidth * imageHeight;
# print "MismatchCount: "+str(mismatchCount)
# print "PixelCount: "+str(pixelCount)
return (float(mismatchCount) / float(pixelCount) ) < 0.01

View File

@ -1,9 +1,11 @@
# -*- coding: utf-8 -*-
'''
test_qgscomposerhtml.py
test_qgscomposerhtml.py
--------------------------------------
Date : August 2012
Copyright : (C) 2012 by Dr. Horst Düster / Dr. Marco Hugentobler
Copyright : (C) 2012 by Dr. Horst Düster /
Dr. Marco Hugentobler
Tim Sutton
email : marco@sourcepole.ch
***************************************************************************
* *
@ -13,66 +15,107 @@ test_qgscomposerhtml.py
* (at your option) any later version. *
* *
***************************************************************************/
'''
'''
import unittest
from utilities import *
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qgis.core import *
import os
from utilities import unitTestDataPath, getQgisTestApp
from PyQt4.QtCore import QUrl, QString, qDebug
from qgis.core import (QgsComposition,
QgsComposerHtml,
QgsComposerFrame,
QgsComposerMultiFrame)
from qgscompositionchecker import QgsCompositionChecker
QGISAPP, CANVAS, IFACE, PARENT = getQgisTestApp()
TEST_DATA_DIR = unitTestDataPath()
class TestQgsComposerMap(unittest.TestCase):
def testCase(self):
self.mComposition = QgsComposition( None )
self.mComposition.setPaperSize( 297, 210 ) #A4 landscape
self.table()
self.tableMultiFrame()
def table(self):
TEST_DATA_DIR = unitTestDataPath()
htmlItem = QgsComposerHtml( self.mComposition, False )
htmlFrame = QgsComposerFrame( self.mComposition, htmlItem, 0, 0, 100, 200 )
htmlFrame.setFrameEnabled( True )
htmlItem.addFrame( htmlFrame )
htmlItem.setUrl( QUrl( QString( "file:///%1" ).arg( QString( TEST_DATA_DIR ) + QDir.separator() + "html_table.html" ) ) );
checker = QgsCompositionChecker( )
result = checker.testComposition( "Composer html table", self.mComposition, QString( TEST_DATA_DIR + QDir.separator().toAscii() + "control_images" + QDir.separator().toAscii() + "expected_composerhtml" + QDir.separator().toAscii() + "composerhtml_table.png" ) )
self.mComposition.removeMultiFrame( htmlItem )
del htmlItem
assert result == True
def tableMultiFrame(self):
TEST_DATA_DIR = unitTestDataPath()
htmlItem = QgsComposerHtml( self.mComposition, False )
htmlFrame = QgsComposerFrame( self.mComposition, htmlItem, 10, 10, 100, 50 )
htmlItem.addFrame( htmlFrame )
htmlItem.setResizeMode( QgsComposerMultiFrame.RepeatUntilFinished )
htmlItem.setUrl( QUrl( QString( "file:///%1" ).arg( QString( TEST_DATA_DIR ) + QDir.separator() + "html_table.html" ) ) )
htmlItem.frame( 0 ).setFrameEnabled( True )
def setUp(self):
"""Run before each test."""
self.mComposition = QgsComposition(None)
self.mComposition.setPaperSize(297, 210) #A4 landscape
self.htmlItem = QgsComposerHtml(self.mComposition, False)
def tearDown(self):
"""Run after each test."""
print "Tear down"
if self.htmlItem:
self.mComposition.removeMultiFrame(self.htmlItem)
del self.htmlItem
def controlImagePath(self, theImageName):
"""Helper to get the path to a control image."""
myPath = os.path.join(TEST_DATA_DIR,
"control_images",
"expected_composerhtml",
theImageName)
assert os.path.exists(myPath)
return myPath
def htmlUrl(self):
"""Helper to get the url of the html doc."""
myPath = os.path.join(TEST_DATA_DIR, "html_table.html")
myUrl = QUrl(QString("file:///%1").arg(myPath))
return myUrl
def testTable(self):
"""Test we can render a html table in a single frame."""
htmlFrame = QgsComposerFrame(self.mComposition,
self.htmlItem, 0, 0, 100, 200)
htmlFrame.setFrameEnabled(True)
self.htmlItem.addFrame(htmlFrame)
self.htmlItem.setUrl(self.htmlUrl())
checker = QgsCompositionChecker()
myResult, myMessage = checker.testComposition(
"Composer html table",
self.mComposition,
self.controlImagePath("composerhtml_table.png"))
qDebug(myMessage)
assert myResult, myMessage
def testTableMultiFrame(self):
"""Test we can render to multiframes."""
htmlFrame = QgsComposerFrame(self.mComposition, self.htmlItem,
10, 10, 100, 50)
self.htmlItem.addFrame(htmlFrame)
self.htmlItem.setResizeMode(QgsComposerMultiFrame.RepeatUntilFinished)
self.htmlItem.setUrl(self.htmlUrl())
self.htmlItem.frame(0).setFrameEnabled(True)
result = True
#page 1
checker1 = QgsCompositionChecker( )
if not checker1.testComposition( "Composer html table", self.mComposition, QString( QString( TEST_DATA_DIR ) + QDir.separator() + "control_images" + QDir.separator() + "expected_composerhtml" + QDir.separator() + "composerhtml_table_multiframe1.png" ), 0 ):
result = False
checker2 = QgsCompositionChecker( )
if not checker2.testComposition( "Composer html table", self.mComposition, QString( QString( TEST_DATA_DIR ) + QDir.separator() + "control_images" + QDir.separator() + "expected_composerhtml" + QDir.separator() + "composerhtml_table_multiframe2.png" ) , 1 ):
result = False
checker3 = QgsCompositionChecker( )
if not checker3.testComposition( "Composer html table", self.mComposition, QString( QString( TEST_DATA_DIR ) + QDir.separator() + "control_images" + QDir.separator() + "expected_composerhtml" + QDir.separator() + "composerhtml_table_multiframe3.png" ), 2 ):
result = False
self.mComposition.removeMultiFrame( htmlItem )
del htmlItem
assert result == True
myPage = 0
checker1 = QgsCompositionChecker()
myControlImage = self.controlImagePath(
"composerhtml_table_multiframe1.png")
print "Checking page 1"
myResult, myMessage = checker1.testComposition("Composer html table",
self.mComposition,
myControlImage,
myPage)
assert myResult, myMessage
myPage = 1
checker2 = QgsCompositionChecker()
myControlImage = self.controlImagePath(
"composerhtml_table_multiframe2.png")
myResult, myMessage = checker2.testComposition("Composer html table",
self.mComposition,
myControlImage,
myPage)
assert myResult, myMessage
myPage = 2
checker3 = QgsCompositionChecker()
myControlImage = self.controlImagePath(
"composerhtml_table_multiframe3.png")
myResult, myMessage = checker3.testComposition("Composer html table",
self.mComposition,
myControlImage,
myPage)
assert myResult, myMessage
if __name__ == '__main__':
unittest.main()