Warning, /libraries/kreport/src/Mainpage.dox is written in an unsupported language. File is not indexed.
0001 /**
0002 @mainpage KReport
0003
0004 %KReport is a framework for the creation and generation of reports in multiple formats.
0005 The %KReport framework implements reporting functionality for creation
0006 of <a href="https://support.office.com/article/e0869f59-7536-4d19-8e05-7158dcd3681c">reports
0007 in MS Access</a> style. They are also similar to <a href="http://www.sap.com/solution/sme/software/analytics/crystal-reports/index.html">
0008 SAP Crystal Reports</a> and <a href="http://help.filemaker.com/app/answers/detail/a_id/13754">
0009 FileMaker reports</a>.
0010
0011 Reports can be created interactively and programmatically. They can be previewed on screen,
0012 printed, and saved in a variety of formats such as HTML, PDF and OpenDocument.
0013 Reports of this kind offer a way to view, format, and summarize the information.
0014 For example a simple report of contact phone numbers can be prepared, or a more complex
0015 report on sales for different products, regions, and periods of time.
0016
0017 @section usecases Use Cases
0018
0019 A report is often filled with information from a database. There are many use cases:
0020 - The data can be displayed, summarized, sorted and grouped
0021 - Totals can be computed and displayed
0022 - Single or multiple records of data can be placed on a page
0023 - Details for individual data records can be placed in a layout
0024 - Labels can be created
0025 - The various report sections, such as title, header or footer, can be sized to suit
0026 - Reports can be generated on demand, thus eliminating saving them in files for further use
0027
0028 @section concepts Concepts
0029
0030 There are three main concepts in %KReport: designer, data sources, and rendering objects.
0031 The report designer is a visual tool to create report templates by placing items, such as labels or fields, and setting their properties;
0032 data sources provide the data needed to render the report from its template;
0033 and rendering objects define how to layout and render the report to output devices, such as the screen or a printer.
0034
0035 @subsection designer Designer
0036
0037 A report design defines the page size, its margins, and one or more sections that hold items, the smallest unit that tell the engine what data to use and how to render it.
0038 Report designs are saved as XML documents, and can be stored on any medium, for instance files or as part of a KEXI database project, for re-use and transfer.
0039 KReportDesigner is based on the Qt Graphics View framework.
0040
0041 Internally, it uses the following classes to manipulate the report’s design:
0042
0043 - KReportDesigner
0044 - KReportDesignerSection
0045 - KReportDesignerSectionDetail
0046 - KReportDesignerSectionDetailGroup
0047 - KReportSectionData
0048 - KReportUnit
0049
0050 Items, the classes that convert data from sources to rendering objects, are provided through the plugin system, for extensibility, and implement one or more of the following interfaces:
0051
0052 - KReportAsyncItemBase
0053 - KReportDesignerItemBase
0054 - KReportDesignerItemRectBase
0055 - KReportItemBase
0056 - KReportPluginInterface
0057
0058 See \ref kreport_item_plugin for more information on how to implement a plugin for a new item.
0059
0060
0061 @subsection datasources Data Sources
0062
0063 Data sources are classes that adapt data from external sources to something that %KReport can work with.
0064 They are somewhat similar in concept to Qt’s QAbstractItemModel, but they instead have to implement one of the following interfaces:
0065
0066 - KReportDataSource
0067 - KReportScriptSource
0068
0069 %KReport does not provide any public implementation of data sources and applications must do it themselves.
0070 See \ref kreport_data_source for more information on how to implement a new data source.
0071
0072
0073 @subsection renderingobjects Rendering Objects
0074
0075 The items placed when designing the report’s XML are kind of cookie cutters that define how a particular data is to be rendered on the report’s output.
0076 However, the actual placement and styling of data on a rendered document is done by the following classes:
0077
0078 - OROCheckBox
0079 - ORODocument
0080 - OROEllipse
0081 - OROImage
0082 - OROLine
0083 - OROPage
0084 - OROPicture
0085 - OROPrimitive
0086 - ORORect
0087 - OROSection
0088 - OROTextBox
0089
0090 There is a class that takes an XML document with the report’s design from one end, a data source from another, and creates a ORODocument with all rendering object correctly placed inside:
0091
0092 - KReportPreRenderer
0093
0094 The actual rendering to an output device, be it the screen or a file, it is performed by classes that inherit from KReportRendererBase, using an instance of KReportRendererContext to pass any information that the renderer requires.
0095 The access to instances of KReportRendererBase it is necessary to use the following class:
0096
0097 - KReportRendererFactory
0098
0099
0100 @section example Simple Example
0101
0102 The following example loads a report design from an XML file named `report.xml`, pre-renders the report using the data source class in the `examples` directory and, finally, renders the final document to a PDF file named `report.pdf`.
0103
0104 \code
0105 #include "KReportExampleDataSource.h"
0106 #include <KReportDocument>
0107 #include <KReportPreRenderer>
0108 #include <KReportRenderObjects>
0109 #include <KReportRendererBase>
0110 #include <QApplication>
0111 #include <QDomDocument>
0112 #include <QDomElement>
0113 #include <QFile>
0114 #include <QPainter>
0115 #include <QPdfWriter>
0116
0117 int main(int argc, char *argv[])
0118 {
0119 QApplication app(argc, argv);
0120
0121 QFile file("report.xml");
0122 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
0123 return EXIT_FAILURE;
0124 }
0125 QDomDocument doc;
0126 doc.setContent(QString::fromUtf8(file.readAll()));
0127 QDomElement root = doc.documentElement();
0128
0129 KReportPreRenderer preRenderer(root.firstChildElement());
0130 if (!preRenderer.isValid()) {
0131 return EXIT_FAILURE;
0132 }
0133 preRenderer.setDataSource(new KReportExampleDataSource);
0134 preRenderer.setName("kreport");
0135 if (!preRenderer.generateDocument()) {
0136 return EXIT_FAILURE;
0137 }
0138
0139 KReportRendererFactory factory;
0140 KReportRendererBase *renderer = factory.createInstance("screen");
0141 if (!renderer) {
0142 return EXIT_FAILURE;
0143 }
0144
0145 const KReportDocument *document = preRenderer.reportData();
0146 QPdfWriter pdfWriter("report.pdf");
0147 pdfWriter.setResolution(96);
0148 pdfWriter.setPageLayout(document->pageLayout());
0149
0150 QPainter painter(&pdfWriter);
0151 KReportRendererContext context;
0152 context.setPainter(&painter);
0153
0154 ORODocument *preRenderedDocument = preRenderer.document();
0155 int numPages = preRenderedDocument->pageCount();
0156 for (int p = 0; p < numPages; p++) {
0157 if (p > 0) {
0158 pdfWriter.newPage();
0159 }
0160 if (!renderer->render(context, preRenderedDocument, p)) {
0161 return EXIT_FAILURE;
0162 }
0163 }
0164 return EXIT_SUCCESS;
0165 }
0166 \endcode
0167
0168 The XML could be something like this:
0169
0170 \verbatim
0171 <!DOCTYPE kexireport>
0172 <kexireport>
0173 <report:content xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"
0174 xmlns:report="http://kexi-project.org/report/2.0"
0175 xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0">
0176 <report:title>Report</report:title>
0177 <report:grid report:grid-divisions="4"
0178 report:grid-visible="1"
0179 report:page-unit="cm"
0180 report:grid-snap="1" />
0181 <report:page-style report:print-orientation="portrait"
0182 fo:margin-right="28.346505829687491pt"
0183 fo:margin-left="28.346505829687491pt"
0184 report:page-size="A4"
0185 fo:margin-top="28.346505829687491pt"
0186 fo:margin-bottom="28.346505829687491pt"
0187 >predefined</report:page-style>
0188 <report:body>
0189 <report:detail>
0190 <report:section fo:background-color="#ffffff"
0191 report:section-type="detail"
0192 svg:height="28.346505829689765pt">
0193 <report:field report:item-data-source="project"
0194 report:z-index="0"
0195 report:vertical-align="center"
0196 report:name="field26"
0197 report:value=""
0198 svg:width="327.750000343254840pt"
0199 report:word-wrap="1"
0200 svg:x="92.250000096614059pt"
0201 svg:y="6.750000007069322pt"
0202 svg:height="12.750000013353164pt"
0203 report:can-grow="1"
0204 report:horizontal-align="left">
0205 <report:text-style fo:background-color="#ffffff"
0206 fo:background-opacity="0%"
0207 fo:letter-spacing="0%"
0208 fo:font-size="9"
0209 fo:font-family="Sans Serif"
0210 fo:foreground-color="#000000"
0211 style:letter-kerning="true"/>
0212 <report:line-style report:line-style="nopen"
0213 report:line-weight="1"
0214 report:line-color="#000000"/>
0215 </report:field>
0216 </report:section>
0217 </report:detail>
0218 </report:body>
0219 </report:content>
0220 </kexireport>
0221 \endverbatim
0222
0223
0224 Project's home page: https://community.kde.org/KReport
0225
0226 @authors
0227 - Adam Pigg <adam@piggz.co.uk> - Maintainer
0228 - Jarosław Staniek <staniek@kde.org> - Developer
0229 - Wojciech Kosowicz <pcellix@gmail.com> - improvements
0230 - Shreya Pandit <shreya@shreyapandit.com> - Web page element
0231 - Radoslaw Wicik <rockford@wicik.pl> - Map element
0232 - OpenMFG, LLC <http://xtuple.com/contact> - original code
0233
0234 Contributors:
0235 - Friedrich W. H. Kossebau <kossebau@kde.org> - cleanups
0236 - Dag Andersen <danders@get2net.dk> - cleanups
0237
0238 @licenses
0239 @lgpl
0240
0241 @page kreport_item_plugin How to Implement a Type Plugin
0242
0243 This section describes how to create your own item types for KReport by implementing a sample plugin that adds a filled rectangle to the report.
0244
0245 - \ref kreport_item_plugin_item_base
0246 - \ref kreport_item_plugin_designer_item
0247 - \ref kreport_item_plugin_scripting
0248 - \ref kreport_item_plugin_plugin
0249
0250 @section kreport_item_plugin_item_base Base Item
0251
0252 The duties of the base item is to extract values of its properties, such as position or size, from the report’s XML, and to create the rendering primitives to draw from the given data.
0253
0254 %KReport will give us the XML data in the form of a QDomNode object that only contains the fragment relevant to our item.
0255 In this case, the XML structure will be something like the following:
0256
0257 \verbatim
0258 <report:rect report:name="rect1"
0259 report:z-index="0"
0260 svg:x="113.386023318759058pt"
0261 svg:y="21.259879372267321pt"
0262 svg:height="40.500000848363968pt"
0263 svg:width="210.000004398924290pt"
0264 fo:background-color="#ffffff"
0265 fo:background-opacity="0%"
0266 >
0267 <report:line-style report:line-style="solid"
0268 report:line-weight="1"
0269 report:line-color="#000000"
0270 />
0271 </report:rect>
0272 \endverbatim
0273
0274 As for the rendering primitives, there are two type of items: these that create them based on a simple value from the data source, for instance a label, and these that use a subquery to extract the data, as is the case for charts;
0275 the former need to implement KReportItemBase::renderSimpleData while the latter need KReportItemBase::renderReportData.
0276 Rendering a rectangle does not need data from a source and will be a simple item that ignores its input.
0277
0278 \code
0279 #include <KReportItemBase>
0280
0281 class QDomNode;
0282
0283 class KReportRectItem : public KReportItemBase
0284 {
0285 Q_OBJECT
0286
0287 public:
0288 KReportRectItem();
0289 explicit KReportRectItem(const QDomNode &node);
0290 ~KReportRectItem() override;
0291
0292 QString typeName() const override;
0293 int renderSimpleData(OROPage *page,
0294 OROSection *section,
0295 const QPointF &offset,
0296 const QVariant &data,
0297 KReportScriptHandler *script) override;
0298
0299 protected:
0300 void createProperties() override;
0301 QBrush brush() const;
0302 KReportLineStyle lineStyle() const;
0303 QPen pen() const;
0304
0305 KProperty *m_backgroundColor;
0306 KProperty *m_backgroundOpacity;
0307 KProperty *m_lineColor;
0308 KProperty *m_lineStyle;
0309 KProperty *m_lineWeight;
0310 }
0311
0312 \endcode
0313
0314 As we will see later, the default constructor will be used when creating an empty item from the designer and it must create all properties except for the name, size, position, and data source, because these are created by KReportItemBase:
0315
0316 \code
0317 #include "kreportrectitem.h"
0318 #include <KProperty>
0319 #include <KPropertySet>
0320
0321 KReportRectItem::KReportRectItem()
0322 : KReportItemBase()
0323 {
0324 createProperties();
0325 }
0326
0327 void KReportRectItem::createProperties()
0328 {
0329 m_backgroundColor = new KProperty("background-color", QColor(Qt::white), tr("Background color"));
0330
0331 m_backgroundOpacity = new KProperty("background-opacity", QVariant(0), tr("Background Opacity"));
0332 m_backgroundOpacity->setOption("max", 100);
0333 m_backgroundOpacity->setOption("min", 0);
0334 m_backgroundOpacity->setOption("suffix", "%");
0335
0336 m_lineWeight = new KProperty("line-weight", 1.0, tr("Line Weight"));
0337 m_lineWeight->setOption("step", 1.0);
0338 m_lineColor = new KProperty("line-color", QColor(Qt::black), tr("Line Color"));
0339 m_lineStyle = new KProperty("line-style", static_cast<int>(Qt::SolidLine), tr("Line Style"), QString(), KProperty::LineStyle);
0340
0341 propertySet()->addProperty(m_backgroundColor);
0342 propertySet()->addProperty(m_backgroundOpacity);
0343 propertySet()->addProperty(m_lineWeight);
0344 propertySet()->addProperty(m_lineColor);
0345 propertySet()->addProperty(m_lineStyle);
0346 }
0347 \endcode
0348
0349 As you can see, we create a KProperty object for each property that can be set and then we add them all to the item’s property set, that will manage their life cycle and signals.
0350
0351 The constructor accepting a QDomNode must extract the properties’ value from it.
0352 Fortunately, KReportItemBase and KReportUtils already contain functions to help us in reading common properties.
0353
0354 \code
0355 #include <KReportUtils>
0356 #include <QDebug>
0357 #include <QDomNode>
0358
0359 KReportRectItem::KReportRectItem(const QDomNode &node)
0360 : KReportRectItem()
0361 {
0362 QDomElement element = node.toElement();
0363 nameProperty()->setValue(KReportUtils::readNameAttribute(element));
0364 setZ(KReportUtils::readZAttribute(element));
0365 parseReportRect(element);
0366
0367 QColor backgroundColor(element.attribute("fo:background-color", "#ffffff"));
0368 m_backgroundColor->setValue(backgroundColor);
0369
0370 bool ok;
0371 int backgroundOpacity = KReportUtils::readPercent(element, "fo:background-opacity", 100, &ok);
0372 if (ok) {
0373 m_backgroundOpacity->setValue(backgroundOpacity);
0374 }
0375
0376 QDomNodeList children = element.childNodes();
0377 for (int i = 0; i < children.count(); i++) {
0378 QDomNode node = children.at(i);
0379 QString name = node.nodeName();
0380
0381 if (name == "report:line-style") {
0382 KReportLineStyle style;
0383 if (parseReportLineStyleData(node.toElement(), &style)) {
0384 m_lineWeight->setValue(style.weight());
0385 m_lineColor->setValue(style.color());
0386 m_lineStyle->setValue(static_cast<int>(style.penStyle()));
0387 }
0388 } else {
0389 qWarning() << "found unknown element while parsing rect element:" << name;
0390 }
0391 }
0392 }
0393 \endcode
0394
0395 Notice how we can call nameProperty() to set this item’s name even though we did not create that property.
0396 Also, parseReportRect will read the `svg:x`, `svg:y`, `svg:width` and `svg:height` attributes from the node’s element and initialize the corresponding position and size properties that we have inherited from KReportItemBase.
0397
0398 Now we are ready to render rectangles to the report’s output.
0399 renderSimpleData receives the page and the section that the item needs to render to add the rendering objects to, and must return how much it stretches the section’s height, for instance as a result of wrapping text.
0400 Both are `nullptr` when %KReport is computing the actual section’s height.
0401
0402 `data` contains the value that the item must render and is given by the data source.
0403 For a rectangle we already got all the information required from the XML and can safely ignore this parameter.
0404
0405 In this case we only need to create an ORORect object and set all its attributes from the item’s properties.
0406 KReportItemBase::scenePosition and KReportItemBase::sceneSize convert, respectively, the position and size from point units to pixels according to the output’s DPI.
0407
0408 \code
0409 #include <KReportUnit>
0410 #include <QPointF>
0411 #include <QRectF>
0412
0413 int KReportRectItem::renderSimpleData(OROPage *page,
0414 OROSection *section,
0415 const QPointF &offset,
0416 const QVariant &data,
0417 KReportScriptHandler *script)
0418 {
0419 Q_UNUSED(data)
0420 Q_UNUSED(script)
0421
0422 auto *rect = new ORORect();
0423 rect->setRect(QRectF(scenePosition(position()) + offset, sceneSize(size())));
0424 rect->setPen(pen());
0425 rect->setBrush(brush());
0426
0427 if (page) {
0428 page->insertPrimitive(rect);
0429 }
0430 if (section) {
0431 OROPrimitive *clone = rect->clone();
0432 clone->setPosition(scenePosition(position()));
0433 section->addPrimitive(clone);
0434 }
0435 if (!page) {
0436 delete rect;
0437 }
0438
0439 return 0;
0440 }
0441
0442 QBrush KReportRectItem::brush() const
0443 {
0444 QColor color = m_backgroundColor->value().value<QColor>();
0445 color.setAlphaF(m_backgroundOpacity->value().toReal() * 0.01);
0446 return QBrush(color);
0447 }
0448
0449 KReportLineStyle KReportRectItem::lineStyle() const
0450 {
0451 KReportLineStyle style;
0452 style.setWeight(m_lineWeight->value().toReal());
0453 style.setColor(m_lineColor->value().value<QColor>());
0454 style.setPenStyle(static_cast<Qt::PenStyle>(m_lineStyle->value().toInt()));
0455 return style;
0456 }
0457
0458 QPen KReportRectItem::pen() const
0459 {
0460 KReportLineStyle style = lineStyle();
0461 return QPen(style.color(), style.weight(), style.penStyle());
0462 }
0463 \endcode
0464
0465 The only remaining bit is to tell %KReport the item‘s name, that will be the basis for its XML tag. That is, `"<report:" + typeName() + ">"`.
0466
0467 \code
0468 QString KReportRectItem::typeName() const
0469 {
0470 return "rect";
0471 }
0472 \endcode
0473
0474
0475 @section kreport_item_plugin_designer_item Designer Item
0476
0477 The designer item extends from the base item and its duties are to build the XML node that will be placed in the report’s structure, and to draw a preview in the designer’s QGraphicsScene.
0478 This object can start its life with the properties at their default values or by initializing their values from a QDomNode, like the base item does.
0479
0480 KReportDesignerItemRectBase is a class that extends KReportDesignerItemBase and is used as the base class for items that are “rectangular”.
0481 This, of course, includes our rectangle, but also items such as fields and labels because they define a rectangular area where they will render its data in.
0482 Extending from this class means that we do not need to take care of handling mouse events and can easily draw the item’s resize handles when it is selected.
0483
0484 \code
0485 #include "kreportrectitem.h"
0486 #include <KReportDesignerItemRectBase>
0487
0488 class KReportRectDesignerItem : public KReportRectItem, public KReportDesignerItemRectBase
0489 {
0490 Q_OBJECT
0491
0492 public:
0493 KReportRectDesignerItem(KReportDesigner *designer, QGraphicsScene *scene, const QPointF &pos);
0494 KReportRectDesignerItem(const QDomNode &node, KReportDesigner *designer, QGraphicsScene *scene);
0495 ~KReportRectDesignerItem() override;
0496
0497 void buildXML(QDomDocument *doc, QDomElement *parent) override;
0498 void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override;
0499 KReportRectDesignerItem *clone() override;
0500
0501 private Q_SLOTS:
0502 void slotPropertyChanged(KPropertySet &set, KProperty &property);
0503
0504 private:
0505 void init(QGraphicsScene *scene);
0506 };
0507 \endcode
0508
0509 The constructor accepting a QDomNode is called when the designer loads an existing document and we simply call the base item’s constructor to initialize the properties from the XML node.
0510 The other constructor is called when the user creates a new item on the designer widget and we need to create a default-valuated instance;
0511 notice how we ask KReportDesigner to suggest us a name based on the item’s type name.
0512 In both cases we have to initialize this item in the scene graph and setup a slot that will inform the designer when a property has changed.
0513
0514 \code
0515 #include "kreportrectdesigneritem.h"
0516 #include <KProperty>
0517 #include <KReportDesigner>
0518 #include <QGraphicsScene>
0519
0520 KReportRectDesignerItem::KReportRectDesignerItem(KReportDesigner *designer,
0521 QGraphicsScene *scene,
0522 const QPointF &pos)
0523 : KReportRectItem()
0524 , KReportDesignerItemRectBase(designer, this)
0525 {
0526 Q_UNUSED(pos)
0527 init(scene);
0528 qreal size = KReportUnit::parseValue("1cm");
0529 setSceneRect(properRect(*designer, size, size));
0530 nameProperty()->setValue(designer->suggestEntityName(typeName()));
0531 }
0532
0533 KReportRectDesignerItem::KReportRectDesignerItem(const QDomNode &node,
0534 KReportDesigner *designer,
0535 QGraphicsScene *scene)
0536 : KReportRectItem(node)
0537 , KReportDesignerItemRectBase(designer, this)
0538 {
0539 init(scene);
0540 setSceneRect(KReportItemBase::scenePosition(item()->position()),
0541 KReportItemBase::sceneSize(item()->size()));
0542 }
0543
0544 void KReportRectDesignerItem::init(QGraphicsScene *scene)
0545 {
0546 if (scene) {
0547 scene->addItem(this);
0548 }
0549
0550 connect(propertySet(),
0551 &KPropertySet::propertyChanged,
0552 this,
0553 &KReportRectDesignerItem::slotPropertyChanged);
0554
0555 setFlags(ItemIsSelectable | ItemIsMovable | ItemSendsGeometryChanges);
0556 setZValue(z());
0557 }
0558
0559 void KReportRectDesignerItem::slotPropertyChanged(KPropertySet &set, KProperty &property) {
0560 if (property.name() == "name") {
0561 if (!designer()->isEntityNameUnique(property.value().toString(), this)) {
0562 property.setValue(oldName());
0563 } else {
0564 setOldName(property.value().toString());
0565 }
0566 }
0567
0568 KReportDesignerItemRectBase::propertyChanged(set, property);
0569 if (designer()) {
0570 designer()->setModified(true);
0571 }
0572 }
0573 \endcode
0574
0575 When the designer wants to write the report’s template in XML, it will call `buildXML()` with the XML document that is creating and the node that needs to be this item’s parent.
0576 Our job is to create the same XML structure that the base item’s constructor accepting a QDomNode can read.
0577 Again, KReportDesignerItemBase has utility functions to help us build the XML of common elements and attributes.
0578
0579 \code
0580 #include <QDomDocument>
0581 #include <QDomElement>
0582
0583 void KReportRectDesignerItem::buildXML(QDomDocument *doc, QDomElement *parent)
0584 {
0585 QDomElement entity = doc->createElement("report:" + typeName());
0586
0587 // properties
0588 addPropertyAsAttribute(&entity, nameProperty());
0589 entity.setAttribute("report:z-index", z());
0590 entity.setAttribute("fo:background-color", m_backgroundColor->value().value<QColor>().name());
0591 entity.setAttribute("fo:background-opacity", QString::number(m_backgroundOpacity->value().toInt()) + '%');
0592
0593 // bounding rect attributes
0594 buildXMLRect(doc, &entity, this);
0595
0596 // line Style element
0597 buildXMLLineStyle(doc, &entity, lineStyle());
0598
0599 parent->appendChild(entity);
0600 }
0601 \endcode
0602
0603 In fact, we can use this dynamic of `buildXML()` generating the XML node that the constructor can read to implement the required `clone` method.
0604 This method is called by KReportDesigner to copy and paste items, among others.
0605
0606 \code
0607 KReportRectDesignerItem *KReportRectDesignerItem::clone()
0608 {
0609 QDomDocument doc;
0610 QDomElement parent = doc.createElement("clone");
0611 buildXML(&doc, &parent);
0612 QDomNode node = parent.firstChild();
0613 return new KReportRectDesignerItem(node, designer(), nullptr);
0614 }
0615 \endcode
0616
0617 The last thing to do is draw the actual item on the designer.
0618 In contrast to KReportItemBase, KReportDesignerItemRectBase is a QGraphicsItem-derived class and must know how to paint itself using a QPainter.
0619 For a rectangle we only have to take the same brush and pen that we use when building a ORORect and paint a rectangle at the current position and size.
0620 KReportDesignerItemRectBase::drawHandles will render the eight small boxes all around the rectangle that allow users to resize our item with the mouse.
0621
0622 \code
0623 void KReportRectDesignerItem::paint(QPainter *painter,
0624 const QStyleOptionGraphicsItem *option,
0625 QWidget *widget)
0626 {
0627 Q_UNUSED(option)
0628 Q_UNUSED(widget)
0629
0630 painter->save();
0631 painter->setPen(KReportRectItem::pen());
0632 painter->setBrush(KReportRectItem::brush());
0633 painter->drawRect(QGraphicsRectItem::rect());
0634 painter->restore();
0635
0636 drawHandles(painter);
0637
0638 }
0639 \endcode
0640
0641 @section kreport_item_plugin_scripting Scripting
0642
0643 %KReport allows users to write scripts in JavaScript to change items’ properties.
0644 For example, the following script for a report named `example_report` would change the background color of an item named rect1 to the color #abc, and toggle the section’s between #ffffff and #dddddd prior to rendering it:
0645
0646 \code
0647 function detail() {
0648 var count = 0;
0649 this.OnRender = function() {
0650 count++;
0651 if (count % 2 == 0) {
0652 example_report.section_detail.backgroundColor = "#ffffff";
0653 } else {
0654 example_report.section_detail.backgroundColor = "#dddddd";
0655 }
0656 example_report.section_detail.objectByName("rect1").backgroundColor = "#abc";
0657 }
0658 }
0659 example_report.section_detail.initialize(new detail());
0660 \endcode
0661
0662 %KReport uses QML’s QJSEngine to run JavaScript and calling `objectByName` from the script will try to create a QObject-derived object that wraps the item whose name is passed as parameter.
0663 This QObject needs to define Qt properties in order to provide a way to write and read item's property values.
0664 By convention, the scripting class is from the `Scripting` namespace.
0665
0666 \code
0667 #include <QObject>
0668
0669 class KReportRectItem;
0670
0671 namespace Scripting {
0672
0673 class Rect : public QObject
0674 {
0675 Q_OBJECT
0676 Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor)
0677 Q_PROPERTY(int backgroundOpacity READ backgroundOpacity WRITE setBackgroundOpacity)
0678 Q_PROPERTY(QColor lineColor READ lineColor WRITE setLineColor)
0679 Q_PROPERTY(int lineStyle READ lineStyle WRITE setLineStyle)
0680 Q_PROPERTY(int lineWeight READ lineWeight WRITE setLineWeight)
0681 Q_PROPERTY(QPointF position READ position WRITE setPosition)
0682 Q_PROPERTY(QSizeF size READ size WRITE setSize)
0683
0684 public:
0685 explicit Rect(KReportRectItem *item, QObject *parent = nullptr);
0686
0687 QColor backgroundColor() const;
0688 int backgroundOpacity() const;
0689 QColor lineColor() const;
0690 int lineStyle() const;
0691 int lineWeight() const;
0692 QPointF position() const;
0693 QSizeF size() const;
0694
0695 public Q_SLOTS:
0696 void setBackgroundColor(const QColor &color);
0697 void setBackgroundOpacity(int opacity);
0698 void setLineColor(const QColor &color);
0699 void setLineStyle(int style);
0700 void setLineWeight(int weight);
0701 void setPosition(const QPointF &position);
0702 void setSize(const QSizeF &size);
0703
0704 private:
0705 KReportRectItem *m_rect;
0706 };
0707
0708 }
0709 \endcode
0710
0711 The implementation is very straightforward and just needs to set or get the item’s property values.
0712
0713 \code
0714 #include "kreportrectscript.h"
0715 #include "kreportrectitem.h"
0716 #include <KProperty>
0717 #include <QPointF>
0718 #include <QSizeF>
0719
0720 Scripting::Rect::Rect(KReportRectItem *item, QObject *parent)
0721 : QObject(parent)
0722 , m_rect(item)
0723 {}
0724
0725 QColor Scripting::Rect::backgroundColor() const
0726 {
0727 return m_rect->m_backgroundColor->value().value<QColor>();
0728 }
0729
0730 int Scripting::Rect::backgroundOpacity() const
0731 {
0732 return m_rect->m_backgroundOpacity->value().toInt();
0733 }
0734
0735 QColor Scripting::Rect::lineColor() const
0736 {
0737 return m_rect->m_lineColor->value().value<QColor>();
0738 }
0739
0740 int Scripting::Rect::lineStyle() const
0741 {
0742 return m_rect->m_lineStyle->value().toInt();
0743 }
0744
0745 int Scripting::Rect::lineWeight() const
0746 {
0747 return m_rect->m_lineWeight->value().toInt();
0748 }
0749
0750 QPointF Scripting::Rect::position() const
0751 {
0752 return m_rect->position();
0753 }
0754
0755 QSizeF Scripting::Rect::size() const
0756 {
0757 return m_rect->size();
0758 }
0759
0760 void Scripting::Rect::setBackgroundColor(const QColor &color)
0761 {
0762 m_rect->m_backgroundColor->setValue(color);
0763 }
0764
0765 void Scripting::Rect::setBackgroundOpacity(int opacity)
0766 {
0767 m_rect->m_backgroundOpacity->setValue(opacity);
0768 }
0769
0770 void Scripting::Rect::setLineColor(const QColor &color)
0771 {
0772 m_rect->m_lineColor->setValue(color);
0773 }
0774
0775 void Scripting::Rect::setLineStyle(int style)
0776 {
0777 m_rect->m_lineStyle->setValue(qMax(1, qMin(style, 5)));
0778 }
0779
0780 void Scripting::Rect::setLineWeight(int weight)
0781 {
0782 m_rect->m_lineWeight->setValue(weight);
0783 }
0784
0785 void Scripting::Rect::setPosition(const QPointF &position)
0786 {
0787 m_rect->setPosition(position);
0788 }
0789
0790 void Scripting::Rect::setSize(const QSizeF &size)
0791 {
0792 m_rect->setSize(size);
0793 }
0794 \endcode
0795
0796 Because most of KReportRectItem properties are private, we need to make `Scripting::Rect` a friend class by appending the following at the end of KReportRectItem’s declaration:
0797
0798 \code
0799 private:
0800 friend class Scripting::Rect;
0801 \endcode
0802
0803
0804 @section kreport_item_plugin_plugin Implementing the Plugin Interface
0805
0806 We already have all the actors ready
0807 — the base item class, the designer’s, and the class that allows changing its properties from scripts —,
0808 but we need a way to somehow tell %KReport that they exist.
0809 This is done by implementing the KReportPluginInterface interface.
0810
0811 \code
0812 #include <KReportPluginInterface>
0813
0814 class Q_DECL_EXPORT KReportRectPlugin : public KReportPluginInterface
0815 {
0816 Q_OBJECT
0817
0818 public:
0819 explicit KReportRectPlugin(QObject *parent, const QVariantList &args = QVariantList());
0820 ~KReportRectPlugin() override;
0821
0822 QObject *createRendererInstance(const QDomNode &element) override;
0823 QObject *createDesignerInstance(const QDomNode &element,
0824 KReportDesigner *designer,
0825 QGraphicsScene *scene) override;
0826 QObject *createDesignerInstance(KReportDesigner *designer,
0827 QGraphicsScene *scene,
0828 const QPointF &pos) override;
0829 #ifdef KREPORT_SCRIPTING
0830 QObject *createScriptInstance(KReportItemBase *item) override;
0831 #endif
0832 };
0833 \endcode
0834
0835 KReportPluginInterface::createRendererInstance is called when there is the need to instantiate a base item for rendering the report.
0836 It is given the QDomNode from the report’s XML template of this item.
0837
0838 \code
0839 #include "kreportrectitem.h"
0840
0841 QObject *KReportRectPlugin::createRendererInstance(const QDomNode &element)
0842 {
0843 return new KReportRectItem(element);
0844 }
0845 \endcode.
0846
0847 Likewise, KReportPluginInterface::createDesignerInstance is called to instantiate a designer item.
0848 As we saw above, the designer item can be needed when loading an XML document or when the user creates a new item in the designer widget, that is why there are two different signatures for this method.
0849
0850 \code
0851 #include "kreportrectdesigneritem.h"
0852
0853 QObject *KReportRectPlugin::createDesignerInstance(const QDomNode &element,
0854 KReportDesigner *designer,
0855 QGraphicsScene *scene)
0856 {
0857 return new KReportRectDesignerItem(element, designer, scene);
0858 }
0859
0860 QObject *KReportRectPlugin::createDesignerInstance(KReportDesigner *designer,
0861 QGraphicsScene *scene,
0862 const QPointF &pos)
0863 {
0864 return new KReportRectDesignerItem(designer, scene, pos);
0865 }
0866 \endcode
0867
0868 The last class we need to instantiate is the QObject-derived object used in scripts.
0869 The method receives the item to wrap as a parameter.
0870 Due to the dynamic nature of JavaScript, we need to cast it to the type of our item and make sure it is correct.
0871
0872 \code
0873 #ifdef KREPORT_SCRIPTING
0874
0875 #include "kreportrectscript.h"
0876
0877 QObject *KReportRectPlugin::createScriptInstance(KReportItemBase *item)
0878 {
0879 auto rect = qobject_cast<KReportRectItem *>(item);
0880 if (!rect) {
0881 return nullptr;
0882 }
0883 return new Scripting::Rect(rect);
0884 }
0885
0886 #endif
0887 \endcode
0888
0889 The last thing required to implement this interface is to create a plugin factory.
0890 It can be done with the K_PLUGIN_CLASS_WITH_JSON macro, that expects the name of the class extending KReportPluginInterface and the name of a JSON file with valid KPlugin metadata.
0891 It is also necessary to include the source code generated by the Qt MOC in the plugin’s implementation file in order to compile the generated factory code.
0892
0893 \code
0894 #include "kreportrectplugin.h"
0895
0896 K_PLUGIN_CLASS_WITH_JSON(KReportRectPlugin, "rect.json")
0897
0898 KReportRectPlugin::KReportRectPlugin(QObject *parent, const QVariantList &args)
0899 : KReportPluginInterface(parent, args)
0900 {}
0901
0902 KReportRectPlugin::~KReportRectPlugin() {}
0903
0904 #include "kreportrectplugin.moc"
0905 \endcode
0906
0907 The JSON file must have all necessary KPlugin metadata.
0908 Also, "Version" must be set to the major and minor component of %KReport’s stable version.
0909 For example, in %KReport 3.2.1 "Version" must be set to "3.2".
0910
0911 Lastly, the JSON file must contain a "X-KDE-PluginInfo-LegacyName" property with the exact same value as the return value of KReportItemBase::typeName or %KReport would fail to find the plugin when reading XML templates or creating new items in the designer.
0912
0913 \verbatim
0914 {
0915 "KPlugin": {
0916 "Authors": [
0917 {
0918 "Email": "author@hosting.suffix",
0919 "Name": "Proud Author"
0920 }
0921 ],
0922 "Category": "",
0923 "Description": "Rectangle element for Reports",
0924 "Icon": "draw-rectangle",
0925 "Id": "org.kde.kreport.rect",
0926 "License": "LGPL",
0927 "Name": "Rect",
0928 "Version": "3.3"
0929 },
0930 "X-KDE-PluginInfo-LegacyName": "rect",
0931 "X-KReport-PluginInfo-Priority": "100"
0932 }
0933 \endverbatim
0934
0935 The last piece you need for a complete item plugin is a `CMakeLists.txt` that compiles and installs the plugin.
0936 The `CMakeLists.txt` looks like the following:
0937
0938 \verbatim
0939 find_package(ECM 1.8.0 NO_MODULE REQUIRED)
0940 set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH})
0941
0942 include(KDEInstallDirs)
0943 include(KDECMakeSettings NO_POLICY_SCOPE)
0944 include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE)
0945
0946 set(CMAKE_INCLUDE_CURRENT_DIR ON)
0947 set(CMAKE_CXX_STANDARD 11)
0948 set(CMAKE_CXX_STANDARD_REQUIRED ON)
0949
0950 find_package(Qt5 5.4.0 COMPONENTS Core REQUIRED)
0951 find_package(KReport 3.2.90 NO_MODULE REQUIRED)
0952
0953 set(kreportrectplugin_SRCS
0954 kreportrectdebug.cpp
0955 kreportrectdesigneritem.cpp
0956 kreportrectitem.cpp
0957 kreportrectplugin.cpp
0958 )
0959
0960 if (KREPORT_SCRIPTING)
0961 list(APPEND kreportrectplugin_SRCS
0962 kreportrectscript.cpp
0963 )
0964 endif(KREPORT_SCRIPTING)
0965
0966 add_library(KReportRectPlugin MODULE ${kreportrectplugin_SRCS})
0967 target_link_libraries(KReportRectPlugin PRIVATE Qt5::Core KReport)
0968
0969 install(TARGETS KReportRectPlugin DESTINATION ${KDE_INSTALL_PLUGINDIR}/kreport3)
0970 \endverbatim
0971
0972 Now you can compile the type plugin and install it.
0973 Once done, the new plugin should be available for every application using %KReport.
0974 In applications that utilize the Report Designer, Items toolbar should also offer the new item type.
0975
0976
0977 @page kreport_data_source How to Implement a Data Source
0978
0979 This section describes how to implement a data source for %KReport.
0980 As an example, this data source will simply wrap a QAbstractTableModel and will keep the current row in an attribute.
0981 It is the most simple data source that does not sort its data.
0982
0983 \code
0984 #include <KReportDataSource>
0985 #include <QPointer>
0986
0987 class QAbstractTableModel;
0988
0989 class KReportTableModelDataSource : public KReportDataSource
0990 {
0991 public:
0992 explicit KReportTableModelDataSource(QAbstractTableModel *model);
0993 ~KReportTableModelDataSource() override;
0994
0995 bool open() override;
0996 bool close() override;
0997 bool moveNext() override;
0998 bool movePrevious() override;
0999 bool moveFirst() override;
1000 bool moveLast() override;
1001 qint64 at() const override;
1002 qint64 recordCount() const override;
1003 int fieldNumber(const QString &field) const override;
1004 QStringList fieldNames() const override;
1005 QVariant value(int pos) const override;
1006 QVariant value(const QString &field) const override;
1007 QStringList dataSourceNames() const override;
1008
1009 private:
1010 qint64 m_currentRow;
1011 QPointer<QAbstractTableModel> m_model;
1012 };
1013 \endcode
1014
1015 The constructor already receives the model with all the data that we need to extract, therefore in this case there is nothing to open or close.
1016
1017 \code
1018 #include "kreporttablemodeldatasource.h"
1019 #include <QAbstractTableModel>
1020
1021 KReportTableModelDataSource::KReportTableModelDataSource(QAbstractTableModel *model)
1022 : KReportDataSource()
1023 , m_currentRow(0)
1024 , m_model(&model)
1025 {}
1026
1027 KReportTableModelDataSource::~KReportTableModelDataSource() {}
1028
1029 bool KReportTableModelDataSource::open()
1030 {
1031 // Nothing to do.
1032 return true;
1033 }
1034
1035 bool KReportTableModelDataSource::close()
1036 {
1037 // Nothing to do.
1038 return true;
1039 }
1040 \endcode
1041
1042 Next we will manage the current row by implementing the next, previous, first, and last methods.
1043 Those methods are called by %KReport when traversing the data source within a report’s section.
1044
1045 \code
1046 bool KReportTableModelDataSource::moveNext()
1047 {
1048 if (m_currentRow >= recordCount() - 1) {
1049 return false;
1050 }
1051 m_currentRow++;
1052 return true;
1053 }
1054
1055 bool KReportTableModelDataSource::movePrevious()
1056 {
1057 if (m_currentRow <= 0) {
1058 return false;
1059 }
1060 m_currentRow--;
1061 return true;
1062 }
1063
1064 bool KReportTableModelDataSource::moveFirst()
1065 {
1066 m_currentRow = 0;
1067 return true;
1068 }
1069
1070 bool KReportTableModelDataSource::moveLast()
1071 {
1072 m_currentRow = recordCount() - 1;
1073 return true;
1074 }
1075
1076 qint64 KReportTableModelDataSource::at() const
1077 {
1078 return m_currentRow;
1079 }
1080 \endcode
1081
1082 %KReport needs to know how many rows there are in the data source.
1083
1084 \code
1085 qint64 KReportTableModelDataSource::recordCount() const
1086 {
1087 return m_model->rowCount();
1088 }
1089 \endcode
1090
1091 It also needs to know the field count for each record, their names, and a way to reference them.
1092 All records need to have the same number of fields, exactly like the rows of a QAbstractTableModel.
1093 In this case, the field names will correspond to column’s names, and will use them as keys;
1094 the default behaviour of KReportDataSource::fieldKeys() is to return the output of KReportDataSource::fieldNames().
1095
1096 \code
1097 QStringList KReportTableModelDataSource::fieldNames() const
1098 {
1099 QStringList names;
1100 for (int i = 0; i < m_model->columnCount(); i++) {
1101 names << m_model->headerData(i, Qt::Horizontal).toString();
1102 }
1103 return names;
1104 }
1105
1106 int KReportTableModelDataSource::fieldNumber(const QString &field) const
1107 {
1108 for (int i = 0; i < m_model->columnCount(); i++) {
1109 if (m_model->headerData(i, Qt::Horizontal).toString() == field) {
1110 return i;
1111 }
1112 }
1113 return -1;
1114 }
1115 \endcode
1116
1117 %KReport will need to be able to read the value of a given field.
1118 The field is always in the current record and can be referenced by the key — the column’s name in this case — or by its position.
1119 Therefore we need to handle both cases.
1120
1121 \code
1122 QVariant KReportTableModelDataSource::value(int pos) const
1123 {
1124 return m_model->data(m_model->index(m_currentRow, pos));
1125 }
1126
1127 QVariant KReportTableModelDataSource::value(const QString &field) const
1128 {
1129 return value(fieldNumber(field));
1130 }
1131 \endcode
1132
1133 The last remaining method to implement is for data sources that can retrieve data from multiple sources.
1134 In this case there is one possible source, the QAbstractTableModel, and have nothing to return.
1135
1136 \code
1137 QStringList KReportTableModelDataSource::dataSourceNames() const
1138 {
1139 return QStringList();
1140 }
1141 \endcode
1142
1143 Our data source is now ready to be used to render reports.
1144 */
1145 // DOXYGEN_SET_PROJECT_NAME = KReport
1146 // DOXYGEN_SET_IGNORE_PREFIX = KReport
1147 // DOXYGEN_SET_EXCLUDE_PATTERNS += *_p.h
1148 // DOXYGEN_SET_EXCLUDE_PATTERNS += */editors/* */plugins/*
1149