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