Skip to content

Commit 09fe865

Browse files
authored
Merge pull request #9616 from elpaso/html-widget
HTML form widget
2 parents bfd28b2 + 0aa326b commit 09fe865

15 files changed

+579
-9
lines changed

python/core/auto_generated/qgsattributeeditorelement.sip.in

+41-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ layer.
4848
AeTypeField,
4949
AeTypeRelation,
5050
AeTypeInvalid,
51-
AeTypeQmlElement
51+
AeTypeQmlElement,
52+
AeTypeHtmlElement
5253
};
5354

5455
QgsAttributeEditorElement( AttributeEditorType type, const QString &name, QgsAttributeEditorElement *parent = 0 );
@@ -394,13 +395,51 @@ The QML code that will be represented within this widget.
394395

395396
void setQmlCode( const QString &qmlCode );
396397
%Docstring
398+
Sets the QML code that will be represented within this widget to ``qmlCode``.
399+
%End
400+
401+
};
402+
403+
404+
class QgsAttributeEditorHtmlElement : QgsAttributeEditorElement
405+
{
406+
%Docstring
407+
An attribute editor widget that will represent arbitrary HTML code.
408+
409+
.. versionadded:: 3.10
410+
%End
411+
412+
%TypeHeaderCode
413+
#include "qgsattributeeditorelement.h"
414+
%End
415+
public:
416+
417+
QgsAttributeEditorHtmlElement( const QString &name, QgsAttributeEditorElement *parent );
418+
%Docstring
419+
Creates a new element which can display HTML
420+
421+
:param name: The name of the widget
422+
:param parent: The parent (used as container)
423+
%End
424+
425+
virtual QgsAttributeEditorElement *clone( QgsAttributeEditorElement *parent ) const /Factory/;
426+
427+
428+
QString htmlCode() const;
429+
%Docstring
397430
The QML code that will be represented within this widget.
398431

399-
@param qmlCode
432+
.. versionadded:: 3.4
433+
%End
434+
435+
void setHtmlCode( const QString &htmlCode );
436+
%Docstring
437+
Sets the HTML code that will be represented within this widget to ``htmlCode``.
400438
%End
401439

402440
};
403441

442+
404443
/************************************************************************
405444
* This file has been generated automatically from *
406445
* *

python/gui/auto_generated/editorwidgets/core/qgswidgetwrapper.sip.in

+3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
%ModuleCode
1616
#include "qgsrelationwidgetwrapper.h"
1717
#include "qgsqmlwidgetwrapper.h"
18+
#include "qgshtmlwidgetwrapper.h"
1819
%End
1920

2021
class QgsWidgetWrapper : QObject
@@ -40,6 +41,8 @@ changed status of the widget will be saved.
4041
sipType = sipType_QgsRelationWidgetWrapper;
4142
else if ( qobject_cast<QgsQmlWidgetWrapper *>( sipCpp ) )
4243
sipType = sipType_QgsQmlWidgetWrapper;
44+
else if ( qobject_cast<QgsHtmlWidgetWrapper *>( sipCpp ) )
45+
sipType = sipType_QgsHtmlWidgetWrapper;
4346
else
4447
sipType = 0;
4548
%End
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/gui/editorwidgets/qgshtmlwidgetwrapper.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
class QgsHtmlWidgetWrapper : QgsWidgetWrapper
11+
{
12+
%Docstring
13+
Wraps a QQuickWidget to display HTML code
14+
15+
.. versionadded:: 3.4
16+
%End
17+
18+
%TypeHeaderCode
19+
#include "qgshtmlwidgetwrapper.h"
20+
%End
21+
public:
22+
23+
QgsHtmlWidgetWrapper( QgsVectorLayer *layer, QWidget *editor, QWidget *parent );
24+
%Docstring
25+
Create a html widget wrapper
26+
27+
:param layer: The layer on which the feature is
28+
:param editor: An editor widget. Can be ``None`` if one should be autogenerated.
29+
:param parent: A parent widget
30+
%End
31+
32+
virtual bool valid() const;
33+
34+
35+
virtual QWidget *createWidget( QWidget *parent );
36+
37+
38+
virtual void initWidget( QWidget *editor );
39+
40+
41+
void reinitWidget();
42+
%Docstring
43+
Clears the content and makes new initialization
44+
%End
45+
46+
void setHtmlCode( const QString &htmlCode );
47+
%Docstring
48+
Sets the HTML code to ``htmlCode``
49+
%End
50+
51+
public slots:
52+
virtual void setFeature( const QgsFeature &feature );
53+
54+
55+
};
56+
57+
58+
59+
/************************************************************************
60+
* This file has been generated automatically from *
61+
* *
62+
* src/gui/editorwidgets/qgshtmlwidgetwrapper.h *
63+
* *
64+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
65+
************************************************************************/

python/gui/gui_auto.sip

+1
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@
287287
%Include auto_generated/editorwidgets/qgsdatetimesearchwidgetwrapper.sip
288288
%Include auto_generated/editorwidgets/qgsdefaultsearchwidgetwrapper.sip
289289
%Include auto_generated/editorwidgets/qgsdoublespinbox.sip
290+
%Include auto_generated/editorwidgets/qgshtmlwidgetwrapper.sip
290291
%Include auto_generated/editorwidgets/qgsmultiedittoolbutton.sip
291292
%Include auto_generated/editorwidgets/qgsrelationreferencesearchwidgetwrapper.sip
292293
%Include auto_generated/editorwidgets/qgsrelationreferencewidget.sip

src/app/qgsattributesformproperties.cpp

+134-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020
#include "qgisapp.h"
2121
#include "qgsfieldcombobox.h"
2222
#include "qgsqmlwidgetwrapper.h"
23+
#include "qgshtmlwidgetwrapper.h"
2324
#include "qgsapplication.h"
2425
#include "qgscolorbutton.h"
26+
#include "qgscodeeditorhtml.h"
2527

2628
QgsAttributesFormProperties::QgsAttributesFormProperties( QgsVectorLayer *layer, QWidget *parent )
2729
: QWidget( parent )
@@ -140,14 +142,17 @@ void QgsAttributesFormProperties::initAvailableWidgetsTree()
140142
}
141143
catitem->setExpanded( true );
142144

143-
// QML widget
145+
// QML/HTML widget
144146
catItemData = DnDTreeItemData( DnDTreeItemData::Container, QStringLiteral( "Other" ), tr( "Other Widgets" ) );
145147
catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
146148

147149
DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::QmlWidget, QStringLiteral( "QmlWidget" ), tr( "QML Widget" ) );
148150
itemData.setShowLabel( true );
149-
150151
mAvailableWidgetsTree->addItem( catitem, itemData );
152+
153+
auto itemDataHtml { DnDTreeItemData( DnDTreeItemData::HtmlWidget, QStringLiteral( "HtmlWidget" ), tr( "HTML Widget" ) ) };
154+
itemDataHtml.setShowLabel( true );
155+
mAvailableWidgetsTree->addItem( catitem, itemDataHtml );
151156
catitem ->setExpanded( true );
152157
}
153158

@@ -479,6 +484,19 @@ QTreeWidgetItem *QgsAttributesFormProperties::loadAttributeEditorTreeItem( QgsAt
479484
newWidget = tree->addItem( parent, itemData );
480485
break;
481486
}
487+
488+
case QgsAttributeEditorElement::AeTypeHtmlElement:
489+
{
490+
const QgsAttributeEditorHtmlElement *htmlElementEditor = static_cast<const QgsAttributeEditorHtmlElement *>( widgetDef );
491+
DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::HtmlWidget, widgetDef->name(), widgetDef->name() );
492+
itemData.setShowLabel( widgetDef->showLabel() );
493+
HtmlElementEditorConfiguration htmlEdConfig;
494+
htmlEdConfig.htmlCode = htmlElementEditor->htmlCode();
495+
itemData.setHtmlElementEditorConfiguration( htmlEdConfig );
496+
newWidget = tree->addItem( parent, itemData );
497+
break;
498+
}
499+
482500
case QgsAttributeEditorElement::AeTypeInvalid:
483501
{
484502
QgsDebugMsg( QStringLiteral( "Not loading invalid attribute editor type..." ) );
@@ -520,6 +538,12 @@ void QgsAttributesFormProperties::onAttributeSelectionChanged()
520538
mAttributeTypeDialog->setVisible( false );
521539
break;
522540
}
541+
case DnDTreeItemData::HtmlWidget:
542+
{
543+
mAttributeRelationEdit->setVisible( false );
544+
mAttributeTypeDialog->setVisible( false );
545+
break;
546+
}
523547

524548
}
525549
}
@@ -620,6 +644,15 @@ QgsAttributeEditorElement *QgsAttributesFormProperties::createAttributeEditorWid
620644
widgetDef = element;
621645
break;
622646
}
647+
648+
case DnDTreeItemData::HtmlWidget:
649+
{
650+
QgsAttributeEditorHtmlElement *element = new QgsAttributeEditorHtmlElement( item->text( 0 ), parent );
651+
element->setHtmlCode( itemData.htmlElementEditorConfiguration().htmlCode );
652+
widgetDef = element;
653+
break;
654+
}
655+
623656
}
624657

625658
widgetDef->setShowLabel( itemData.showLabel() );
@@ -875,6 +908,10 @@ QTreeWidgetItem *DnDTree::addItem( QTreeWidgetItem *parent, QgsAttributesFormPro
875908
case QgsAttributesFormProperties::DnDTreeItemData::QmlWidget:
876909
//no icon for QmlWidget
877910
break;
911+
912+
case QgsAttributesFormProperties::DnDTreeItemData::HtmlWidget:
913+
//no icon for HtmlWidget
914+
break;
878915
}
879916
}
880917
newItem->setData( 0, QgsAttributesFormProperties::DnDTreeRole, data );
@@ -955,6 +992,11 @@ bool DnDTree::dropMimeData( QTreeWidgetItem *parent, int index, const QMimeData
955992
{
956993
onItemDoubleClicked( newItem, 0 );
957994
}
995+
996+
if ( itemElement.type() == QgsAttributesFormProperties::DnDTreeItemData::HtmlWidget )
997+
{
998+
onItemDoubleClicked( newItem, 0 );
999+
}
9581000
}
9591001
}
9601002

@@ -1295,6 +1337,85 @@ void DnDTree::onItemDoubleClicked( QTreeWidgetItem *item, int column )
12951337
}
12961338
break;
12971339

1340+
case QgsAttributesFormProperties::DnDTreeItemData::HtmlWidget:
1341+
{
1342+
QDialog dlg;
1343+
dlg.setWindowTitle( tr( "Configure HTML Widget" ) );
1344+
1345+
QVBoxLayout *mainLayout = new QVBoxLayout();
1346+
QHBoxLayout *htmlLayout = new QHBoxLayout();
1347+
QVBoxLayout *layout = new QVBoxLayout();
1348+
mainLayout->addLayout( htmlLayout );
1349+
htmlLayout->addLayout( layout );
1350+
dlg.setLayout( mainLayout );
1351+
layout->addWidget( baseWidget );
1352+
1353+
QLineEdit *title = new QLineEdit( itemData.name() );
1354+
1355+
//htmlCode
1356+
QgsCodeEditorHTML *htmlCode = new QgsCodeEditorHTML( );
1357+
htmlCode->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
1358+
htmlCode->setText( itemData.htmlElementEditorConfiguration().htmlCode );
1359+
1360+
QgsHtmlWidgetWrapper *htmlWrapper = new QgsHtmlWidgetWrapper( mLayer, nullptr, this );
1361+
QgsFeature previewFeature;
1362+
mLayer->getFeatures().nextFeature( previewFeature );
1363+
1364+
//update preview on text change
1365+
connect( htmlCode, &QgsCodeEditorHTML::textChanged, this, [ = ]
1366+
{
1367+
htmlWrapper->setHtmlCode( htmlCode->text( ) );
1368+
htmlWrapper->reinitWidget();
1369+
htmlWrapper->setFeature( previewFeature );
1370+
} );
1371+
1372+
QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1373+
expressionWidget->setLayer( mLayer );
1374+
QToolButton *addExpressionButton = new QToolButton();
1375+
addExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1376+
1377+
connect( addExpressionButton, &QAbstractButton::clicked, this, [ = ]
1378+
{
1379+
htmlCode->insertText( QStringLiteral( "<script>document.write(expression.evaluate(\"%1\"));</script>" ).arg( expressionWidget->expression().replace( '"', QLatin1String( "\\\"" ) ) ) );
1380+
} );
1381+
1382+
layout->addWidget( new QLabel( tr( "Title" ) ) );
1383+
layout->addWidget( title );
1384+
QGroupBox *expressionWidgetBox = new QGroupBox( tr( "HTML Code" ) );
1385+
layout->addWidget( expressionWidgetBox );
1386+
expressionWidgetBox->setLayout( new QHBoxLayout );
1387+
expressionWidgetBox->layout()->addWidget( expressionWidget );
1388+
expressionWidgetBox->layout()->addWidget( addExpressionButton );
1389+
layout->addWidget( htmlCode );
1390+
QScrollArea *htmlPreviewBox = new QScrollArea();
1391+
htmlPreviewBox->setLayout( new QGridLayout );
1392+
htmlPreviewBox->setMinimumWidth( 400 );
1393+
htmlPreviewBox->layout()->addWidget( htmlWrapper->widget() );
1394+
//emit to load preview for the first time
1395+
emit htmlCode->textChanged();
1396+
htmlLayout->addWidget( htmlPreviewBox );
1397+
1398+
QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1399+
1400+
connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1401+
connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1402+
1403+
mainLayout->addWidget( buttonBox );
1404+
1405+
if ( dlg.exec() )
1406+
{
1407+
QgsAttributesFormProperties::HtmlElementEditorConfiguration htmlEdCfg;
1408+
htmlEdCfg.htmlCode = htmlCode->text();
1409+
itemData.setName( title->text() );
1410+
itemData.setHtmlElementEditorConfiguration( htmlEdCfg );
1411+
itemData.setShowLabel( showLabelCheckbox->isChecked() );
1412+
1413+
item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1414+
item->setText( 0, title->text() );
1415+
}
1416+
}
1417+
break;
1418+
12981419
case QgsAttributesFormProperties::DnDTreeItemData::Field:
12991420
{
13001421
QDialog dlg;
@@ -1407,6 +1528,17 @@ void QgsAttributesFormProperties::DnDTreeItemData::setQmlElementEditorConfigurat
14071528
mQmlElementEditorConfiguration = qmlElementEditorConfiguration;
14081529
}
14091530

1531+
1532+
QgsAttributesFormProperties::HtmlElementEditorConfiguration QgsAttributesFormProperties::DnDTreeItemData::htmlElementEditorConfiguration() const
1533+
{
1534+
return mHtmlElementEditorConfiguration;
1535+
}
1536+
1537+
void QgsAttributesFormProperties::DnDTreeItemData::setHtmlElementEditorConfiguration( QgsAttributesFormProperties::HtmlElementEditorConfiguration htmlElementEditorConfiguration )
1538+
{
1539+
mHtmlElementEditorConfiguration = htmlElementEditorConfiguration;
1540+
}
1541+
14101542
QColor QgsAttributesFormProperties::DnDTreeItemData::backgroundColor() const
14111543
{
14121544
return mBackgroundColor;

0 commit comments

Comments
 (0)