20#include "qgsattributetypedialog.h"
41#include <QPlainTextEdit>
48#include "moc_qgsattributesformview.cpp"
50using namespace Qt::StringLiterals;
59 if ( selectionModel()->selectedRows( 0 ).count() == 0 )
62 return mModel->mapToSource( selectionModel()->selectedRows( 0 ).at( 0 ) );
81 QModelIndex index =
mModel->mapFromSource( model->firstRecursiveMatchingModelIndex( itemType, itemId ) );
83 if ( index.isValid() )
87 const QModelIndexList selectedIndexes = selectionModel()->selectedRows( 0 );
88 if ( !( selectedIndexes.count() == 1 && selectedIndexes.at( 0 ) == index ) )
90 selectionModel()->setCurrentIndex( index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
95 selectionModel()->clearSelection();
101 mModel->setFilterText( text );
122 viewport()->repaint();
125 viewport()->repaint();
137 const QList<QgsAttributesFormItem *> keys =
mIndicators.keys();
149 return mModel->sourceAttributesFormModel();
159 mModel = qobject_cast<QgsAttributesFormProxyModel *>( model );
163 QTreeView::setModel(
mModel );
175 connect(
this, &QTreeView::doubleClicked,
this, &QgsAttributesFormLayoutView::onItemDoubleClicked );
180 mModel = qobject_cast<QgsAttributesFormProxyModel *>( model );
184 QTreeView::setModel(
mModel );
192void QgsAttributesFormLayoutView::handleExternalDroppedItem( QModelIndex &index )
194 selectionModel()->setCurrentIndex(
mModel->mapFromSource( index ), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
200 onItemDoubleClicked(
mModel->mapFromSource( index ) );
204void QgsAttributesFormLayoutView::handleInternalDroppedItem( QModelIndex &index )
206 selectionModel()->clearCurrentIndex();
210 expandRecursively(
mModel->mapFromSource( index ) );
216 const QMimeData *data =
event->mimeData();
218 if ( data->hasFormat( u
"application/x-qgsattributesformavailablewidgetsrelement"_s ) || data->hasFormat( u
"application/x-qgsattributesformlayoutelement"_s ) )
221 if ( event->source() ==
this )
223 event->setDropAction( Qt::MoveAction );
231 QTreeView::dragEnterEvent( event );
240 const QMimeData *data =
event->mimeData();
242 if ( data->hasFormat( u
"application/x-qgsattributesformavailablewidgetsrelement"_s ) || data->hasFormat( u
"application/x-qgsattributesformlayoutelement"_s ) )
245 if ( event->source() ==
this )
247 event->setDropAction( Qt::MoveAction );
255 QTreeView::dragMoveEvent( event );
260 if ( !( event->mimeData()->hasFormat( u
"application/x-qgsattributesformavailablewidgetsrelement"_s ) || event->mimeData()->hasFormat( u
"application/x-qgsattributesformlayoutelement"_s ) ) )
263 if ( event->source() ==
this )
265 event->setDropAction( Qt::MoveAction );
268 QTreeView::dropEvent( event );
271void QgsAttributesFormLayoutView::onItemDoubleClicked(
const QModelIndex &index )
273 QModelIndex sourceIndex =
mModel->mapToSource( index );
278 QGroupBox *baseData =
new QGroupBox( tr(
"Base configuration" ) );
280 QFormLayout *baseLayout =
new QFormLayout();
281 baseData->setLayout( baseLayout );
282 QCheckBox *showLabelCheckbox =
new QCheckBox( u
"Show label"_s );
283 showLabelCheckbox->setChecked( itemData.
showLabel() );
284 baseLayout->addRow( showLabelCheckbox );
285 QWidget *baseWidget =
new QWidget();
286 baseWidget->setLayout( baseLayout );
300 dlg.setObjectName(
"QML Form Configuration Widget" );
302 dlg.setWindowTitle( tr(
"Configure QML Widget" ) );
304 QVBoxLayout *mainLayout =
new QVBoxLayout( &dlg );
305 QSplitter *qmlSplitter =
new QSplitter();
306 QWidget *qmlConfigWiget =
new QWidget();
307 QVBoxLayout *layout =
new QVBoxLayout( qmlConfigWiget );
308 layout->setContentsMargins( 0, 0, 0, 0 );
309 mainLayout->addWidget( qmlSplitter );
310 qmlSplitter->addWidget( qmlConfigWiget );
311 layout->addWidget( baseWidget );
313 QLineEdit *title =
new QLineEdit( itemName );
326 QComboBox *qmlObjectTemplate =
new QComboBox();
327 qmlObjectTemplate->addItem( tr(
"Free Text…" ) );
328 qmlObjectTemplate->addItem( tr(
"Rectangle" ) );
329 qmlObjectTemplate->addItem( tr(
"Pie Chart" ) );
330 qmlObjectTemplate->addItem( tr(
"Bar Chart" ) );
331 connect( qmlObjectTemplate, qOverload<int>( &QComboBox::currentIndexChanged ), qmlCode, [qmlCode](
int index ) {
342 qmlCode->
setText( QStringLiteral(
343 "import QtQuick 2.0\n"
348 " color: \"steelblue\"\n"
349 " Text{ text: \"A rectangle\" }\n"
356 qmlCode->
setText( QStringLiteral(
357 "import QtQuick 2.0\n"
358 "import QtCharts 2.0\n"
366 " PieSlice { label: \"First slice\"; value: 25 }\n"
367 " PieSlice { label: \"Second slice\"; value: 45 }\n"
368 " PieSlice { label: \"Third slice\"; value: 30 }\n"
376 qmlCode->
setText( QStringLiteral(
377 "import QtQuick 2.0\n"
378 "import QtCharts 2.0\n"
381 " title: \"Bar series\"\n"
384 " legend.alignment: Qt.AlignBottom\n"
385 " antialiasing: true\n"
394 " axisY: valueAxisY\n"
395 " axisX: BarCategoryAxis { categories: [\"2007\", \"2008\", \"2009\", \"2010\", \"2011\", \"2012\" ] }\n"
396 " BarSet { label: \"Bob\"; values: [2, 2, 3, 4, 5, 6] }\n"
397 " BarSet { label: \"Susan\"; values: [5, 1, 2, 4, 1, 7] }\n"
398 " BarSet { label: \"James\"; values: [3, 5, 8, 13, 5, 8] }\n"
409 QgsFieldExpressionWidget *expressionWidget =
new QgsFieldExpressionWidget;
410 expressionWidget->setButtonVisible(
false );
411 expressionWidget->registerExpressionContextGenerator(
this );
412 expressionWidget->setLayer(
mLayer );
413 QToolButton *addFieldButton =
new QToolButton();
416 QToolButton *editExpressionButton =
new QToolButton();
418 editExpressionButton->setToolTip( tr(
"Insert/Edit Expression" ) );
420 connect( addFieldButton, &QAbstractButton::clicked,
this, [expressionWidget, qmlCode] {
421 QString expression = expressionWidget->expression().trimmed().replace(
'"',
"\\\""_L1 );
422 if ( !expression.isEmpty() )
423 qmlCode->
insertText( u
"expression.evaluate(\"%1\")"_s.arg( expression ) );
426 connect( editExpressionButton, &QAbstractButton::clicked,
this, [
this, qmlCode] {
428 expression.replace(
"\\\""_L1,
"\""_L1 );
430 QgsExpressionBuilderDialog exprDlg(
mLayer, expression,
this, u
"generic"_s, context );
432 exprDlg.setWindowTitle( tr(
"Insert Expression" ) );
433 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
435 QString expression = exprDlg.expressionText().trimmed().replace(
'"',
"\\\""_L1 );
436 if ( !expression.isEmpty() )
437 qmlCode->
insertText( u
"expression.evaluate(\"%1\")"_s.arg( expression ) );
441 layout->addWidget(
new QLabel( tr(
"Title" ) ) );
442 layout->addWidget( title );
443 QGroupBox *qmlCodeBox =
new QGroupBox( tr(
"QML Code" ) );
444 qmlCodeBox->setLayout(
new QVBoxLayout );
445 qmlCodeBox->layout()->addWidget( qmlObjectTemplate );
446 QWidget *expressionWidgetBox =
new QWidget();
447 qmlCodeBox->layout()->addWidget( expressionWidgetBox );
448 expressionWidgetBox->setLayout(
new QHBoxLayout );
449 expressionWidgetBox->layout()->setContentsMargins( 0, 0, 0, 0 );
450 expressionWidgetBox->layout()->addWidget( expressionWidget );
451 expressionWidgetBox->layout()->addWidget( addFieldButton );
452 expressionWidgetBox->layout()->addWidget( editExpressionButton );
453 layout->addWidget( qmlCodeBox );
454 layout->addWidget( qmlCode );
456 QTextEdit *errorFeedbackWidget =
new QTextEdit();
457 errorFeedbackWidget->setMaximumHeight( 90 );
458 layout->addWidget( errorFeedbackWidget );
465 if ( QQuickWidget *qmlWidget =
dynamic_cast<QQuickWidget *
>( qmlWrapper->
widget() ) )
467 const QList<QQmlError> errors = qmlWidget->errors();
468 if ( !errors.isEmpty() )
470 QStringList errorStrings;
471 for (
const QQmlError &error : errors )
473 errorStrings << u
"%1:%2: %3"_s.arg( QString::number( error.line() ), QString::number( error.column() ), error.description() );
475 errorFeedbackWidget->setText( errorStrings.join(
"\n" ) );
479 errorFeedbackWidget->setText( QObject::tr(
"Valid code" ) );
484 QScrollArea *qmlPreviewBox =
new QgsScrollArea();
485 qmlPreviewBox->setMinimumWidth( 200 );
486 qmlPreviewBox->setWidget( qmlWrapper->
widget() );
489 qmlSplitter->addWidget( qmlPreviewBox );
490 qmlSplitter->setChildrenCollapsible(
false );
491 qmlSplitter->setHandleWidth( 6 );
492 qmlSplitter->setSizes( QList<int>() << 1 << 1 );
494 QDialogButtonBox *buttonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
496 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
497 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
498 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [] {
QgsHelp::openHelp( u
"working_with_vector/vector_properties.html#other-widgets"_s ); } );
500 mainLayout->addWidget( buttonBox );
504 QgsAttributesFormData::QmlElementEditorConfiguration qmlEdCfg;
505 qmlEdCfg.
qmlCode = qmlCode->text();
507 itemData.
setShowLabel( showLabelCheckbox->isChecked() );
518 dlg.setObjectName(
"HTML Form Configuration Widget" );
520 dlg.setWindowTitle( tr(
"Configure HTML Widget" ) );
522 QVBoxLayout *mainLayout =
new QVBoxLayout( &dlg );
523 QSplitter *htmlSplitter =
new QSplitter();
524 QWidget *htmlConfigWiget =
new QWidget();
525 QVBoxLayout *layout =
new QVBoxLayout( htmlConfigWiget );
526 layout->setContentsMargins( 0, 0, 0, 0 );
527 mainLayout->addWidget( htmlSplitter );
528 htmlSplitter->addWidget( htmlConfigWiget );
529 htmlSplitter->setChildrenCollapsible(
false );
530 htmlSplitter->setHandleWidth( 6 );
531 htmlSplitter->setSizes( QList<int>() << 1 << 1 );
532 layout->addWidget( baseWidget );
534 QLineEdit *title =
new QLineEdit( itemName );
537 QgsCodeEditorHTML *htmlCode =
new QgsCodeEditorHTML();
538 htmlCode->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
541 QgsHtmlWidgetWrapper *htmlWrapper =
new QgsHtmlWidgetWrapper(
mLayer,
nullptr,
this );
542 QgsFeature previewFeature;
543 mLayer->getFeatures().nextFeature( previewFeature );
546 connect( htmlCode, &QgsCodeEditorHTML::textChanged,
this, [htmlWrapper, htmlCode, previewFeature] {
552 QgsFieldExpressionWidget *expressionWidget =
new QgsFieldExpressionWidget;
553 expressionWidget->setButtonVisible(
false );
554 expressionWidget->registerExpressionContextGenerator(
this );
555 expressionWidget->setLayer(
mLayer );
556 QToolButton *addFieldButton =
new QToolButton();
559 QToolButton *editExpressionButton =
new QToolButton();
561 editExpressionButton->setToolTip( tr(
"Insert/Edit Expression" ) );
563 connect( addFieldButton, &QAbstractButton::clicked,
this, [expressionWidget, htmlCode] {
564 QString expression = expressionWidget->expression().trimmed().replace(
'"',
"\\\""_L1 );
565 if ( !expression.isEmpty() )
566 htmlCode->
insertText( u
"<script>document.write(expression.evaluate(\"%1\"));</script>"_s.arg( expression ) );
569 connect( editExpressionButton, &QAbstractButton::clicked,
this, [
this, htmlCode] {
572 expression.replace(
"\\\""_L1,
"\""_L1 );
574 QgsExpressionBuilderDialog exprDlg(
mLayer, expression,
this, u
"generic"_s, context );
576 exprDlg.setWindowTitle( tr(
"Insert Expression" ) );
577 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
579 QString expression = exprDlg.expressionText().trimmed().replace(
'"',
"\\\""_L1 );
580 if ( !expression.isEmpty() )
581 htmlCode->
insertText( u
"<script>document.write(expression.evaluate(\"%1\"));</script>"_s.arg( expression ) );
585 layout->addWidget(
new QLabel( tr(
"Title" ) ) );
586 layout->addWidget( title );
587 QGroupBox *expressionWidgetBox =
new QGroupBox( tr(
"HTML Code" ) );
588 layout->addWidget( expressionWidgetBox );
589 expressionWidgetBox->setLayout(
new QHBoxLayout );
590 expressionWidgetBox->layout()->addWidget( expressionWidget );
591 expressionWidgetBox->layout()->addWidget( addFieldButton );
592 expressionWidgetBox->layout()->addWidget( editExpressionButton );
593 layout->addWidget( htmlCode );
594 QScrollArea *htmlPreviewBox =
new QgsScrollArea();
595 htmlPreviewBox->setLayout(
new QGridLayout );
596 htmlPreviewBox->setMinimumWidth( 200 );
597 htmlPreviewBox->layout()->addWidget( htmlWrapper->
widget() );
599 emit htmlCode->textChanged();
600 htmlSplitter->addWidget( htmlPreviewBox );
601 htmlSplitter->setChildrenCollapsible(
false );
602 htmlSplitter->setHandleWidth( 6 );
603 htmlSplitter->setSizes( QList<int>() << 1 << 1 );
605 QDialogButtonBox *buttonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
607 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
608 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
609 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [] {
QgsHelp::openHelp( u
"working_with_vector/vector_properties.html#other-widgets"_s ); } );
611 mainLayout->addWidget( buttonBox );
615 QgsAttributesFormData::HtmlElementEditorConfiguration htmlEdCfg;
616 htmlEdCfg.
htmlCode = htmlCode->text();
618 itemData.
setShowLabel( showLabelCheckbox->isChecked() );
629 dlg.setObjectName(
"Text Form Configuration Widget" );
631 dlg.setWindowTitle( tr(
"Configure Text Widget" ) );
633 QVBoxLayout *mainLayout =
new QVBoxLayout( &dlg );
634 QSplitter *textSplitter =
new QSplitter();
635 QWidget *textConfigWiget =
new QWidget();
636 QVBoxLayout *layout =
new QVBoxLayout( textConfigWiget );
637 layout->setContentsMargins( 0, 0, 0, 0 );
638 mainLayout->addWidget( textSplitter );
639 textSplitter->addWidget( textConfigWiget );
640 layout->addWidget( baseWidget );
642 QLineEdit *title =
new QLineEdit( itemName );
644 QgsCodeEditorHTML *text =
new QgsCodeEditorHTML();
645 text->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
648 QgsTextWidgetWrapper *textWrapper =
new QgsTextWidgetWrapper(
mLayer,
nullptr,
this );
649 QgsFeature previewFeature;
650 ( void )
mLayer->getFeatures( QgsFeatureRequest().setLimit( 1 ) ).nextFeature( previewFeature );
653 connect( text, &QgsCodeEditorExpression::textChanged,
this, [textWrapper, previewFeature, text] {
654 textWrapper->
setText( text->text() );
659 QgsFieldExpressionWidget *expressionWidget =
new QgsFieldExpressionWidget;
660 expressionWidget->setButtonVisible(
false );
661 expressionWidget->registerExpressionContextGenerator(
this );
662 expressionWidget->setLayer(
mLayer );
663 QToolButton *addFieldButton =
new QToolButton();
666 QToolButton *editExpressionButton =
new QToolButton();
668 editExpressionButton->setToolTip( tr(
"Insert/Edit Expression" ) );
670 connect( addFieldButton, &QAbstractButton::clicked,
this, [expressionWidget, text] {
671 QString expression = expressionWidget->expression().trimmed();
672 if ( !expression.isEmpty() )
673 text->
insertText( u
"[%%1%]"_s.arg( expression ) );
675 connect( editExpressionButton, &QAbstractButton::clicked,
this, [
this, text] {
679 QgsExpressionBuilderDialog exprDlg(
mLayer, expression,
this, u
"generic"_s, context );
681 exprDlg.setWindowTitle( tr(
"Insert Expression" ) );
682 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
684 QString expression = exprDlg.expressionText().trimmed();
685 if ( !expression.isEmpty() )
686 text->
insertText( u
"[%%1%]"_s.arg( expression ) );
690 layout->addWidget(
new QLabel( tr(
"Title" ) ) );
691 layout->addWidget( title );
692 QGroupBox *expressionWidgetBox =
new QGroupBox( tr(
"Text" ) );
693 layout->addWidget( expressionWidgetBox );
694 expressionWidgetBox->setLayout(
new QHBoxLayout );
695 expressionWidgetBox->layout()->addWidget( expressionWidget );
696 expressionWidgetBox->layout()->addWidget( addFieldButton );
697 expressionWidgetBox->layout()->addWidget( editExpressionButton );
698 layout->addWidget( text );
699 QScrollArea *textPreviewBox =
new QgsScrollArea();
700 textPreviewBox->setWidgetResizable(
true );
701 textPreviewBox->setMinimumWidth( 200 );
702 textPreviewBox->setWidget( textWrapper->
widget() );
704 emit text->textChanged();
705 textSplitter->addWidget( textPreviewBox );
706 textSplitter->setChildrenCollapsible(
false );
707 textSplitter->setHandleWidth( 6 );
708 textSplitter->setSizes( QList<int>() << 1 << 1 );
710 QDialogButtonBox *buttonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
712 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
713 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
714 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [] {
QgsHelp::openHelp( u
"working_with_vector/vector_properties.html#other-widgets"_s ); } );
716 mainLayout->addWidget( buttonBox );
720 QgsAttributesFormData::TextElementEditorConfiguration textEdCfg;
721 textEdCfg.
text = text->text();
723 itemData.
setShowLabel( showLabelCheckbox->isChecked() );
734 dlg.setObjectName(
"Spacer Form Configuration Widget" );
736 dlg.setWindowTitle( tr(
"Configure Spacer Widget" ) );
738 QVBoxLayout *mainLayout =
new QVBoxLayout();
739 mainLayout->addWidget(
new QLabel( tr(
"Title" ) ) );
740 QLineEdit *title =
new QLineEdit( itemName );
741 mainLayout->addWidget( title );
743 QHBoxLayout *cbLayout =
new QHBoxLayout();
744 mainLayout->addLayout( cbLayout );
745 dlg.setLayout( mainLayout );
746 QCheckBox *cb =
new QCheckBox { &dlg };
748 cbLayout->addWidget(
new QLabel( tr(
"Draw horizontal line" ), &dlg ) );
749 cbLayout->addWidget( cb );
751 QDialogButtonBox *buttonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
753 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
754 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
755 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [] {
QgsHelp::openHelp( u
"working_with_vector/vector_properties.html#other-widgets"_s ); } );
757 mainLayout->addWidget( buttonBox );
761 QgsAttributesFormData::SpacerElementEditorConfiguration spacerEdCfg;
762 spacerEdCfg.
drawLine = cb->isChecked();
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
A text editor based on QScintilla2.
@ ScriptEditor
Standard mode, allows for display and edit of entire scripts.
void setText(const QString &text) override
void setEditingTimeoutInterval(int timeout)
Sets the timeout (in milliseconds) threshold for the editingTimeout() signal to be emitted after an e...
@ CodeFolding
Indicates that code folding should be enabled for the editor.
void insertText(const QString &text)
Insert text at cursor position, or replace any selected text if user has made a selection.
void setLineNumbersVisible(bool visible)
Sets whether line numbers should be visible in the editor.
void editingTimeout()
Emitted when either:
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * formScope(const QgsFeature &formFeature=QgsFeature(), const QString &formMode=QString())
Creates a new scope which contains functions and variables from the current attribute form/table form...
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
static QString findAndSelectActiveExpression(QgsCodeEditor *editor, const QString &pattern=QString())
Find the expression under the cursor in the given editor and select it.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
static QgsProject * instance()
Returns the QgsProject singleton instance.
void setText(const QString &text)
Sets the text code to htmlCode.
void reinitWidget()
Clears the content and makes new initialization.
void setFeature(const QgsFeature &feature) override
Represents a vector layer which manages a vector based dataset.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.