Skip to content
Permalink
Browse files

[FEATURE] New form widget for binary (blob) fields

This widget is available for binary fields only (and is the default
widget used for binary fields). It offers a label showing
whether the blob field is empty or not, and if non-empty shows
the content size (in bytes/kb/etc).

A drop down menu button allows users to save the current binary
contents of the field out to a disk based file, clear the contents
of a blob field, or embed binary contents by picking a file
from their system.
  • Loading branch information
nyalldawson committed Nov 11, 2018
1 parent 608d03c commit d96ce7a1d7799221b8a557042593b0a21ca388b5
@@ -95,6 +95,8 @@ SET(QGIS_GUI_SRCS
editorwidgets/core/qgssearchwidgetwrapper.cpp
editorwidgets/core/qgswidgetwrapper.cpp

editorwidgets/qgsbinarywidgetfactory.cpp
editorwidgets/qgsbinarywidgetwrapper.cpp
editorwidgets/qgscheckboxconfigdlg.cpp
editorwidgets/qgscheckboxsearchwidgetwrapper.cpp
editorwidgets/qgscheckboxwidgetwrapper.cpp
@@ -657,6 +659,7 @@ SET(QGIS_GUI_MOC_HDRS
editorwidgets/core/qgssearchwidgetwrapper.h
editorwidgets/core/qgswidgetwrapper.h

editorwidgets/qgsbinarywidgetwrapper.h
editorwidgets/qgscheckboxconfigdlg.h
editorwidgets/qgscheckboxsearchwidgetwrapper.h
editorwidgets/qgscheckboxwidgetwrapper.h
@@ -831,6 +834,7 @@ SET(QGIS_GUI_HDRS
editorwidgets/core/qgseditorwidgetfactory.h
editorwidgets/core/qgseditorwidgetautoconf.h

editorwidgets/qgsbinarywidgetfactory.h
editorwidgets/qgscheckboxwidgetfactory.h
editorwidgets/qgsclassificationwidgetwrapperfactory.h
editorwidgets/qgscolorwidgetfactory.h
@@ -23,6 +23,7 @@
#include "qgssearchwidgetwrapper.h"

// Editors
#include "qgsbinarywidgetfactory.h"
#include "qgsclassificationwidgetwrapperfactory.h"
#include "qgscheckboxwidgetfactory.h"
#include "qgscolorwidgetfactory.h"
@@ -63,6 +64,7 @@ void QgsEditorWidgetRegistry::initEditors( QgsMapCanvas *mapCanvas, QgsMessageBa
registerWidget( QStringLiteral( "ExternalResource" ), new QgsExternalResourceWidgetFactory( tr( "Attachment" ) ) );
registerWidget( QStringLiteral( "KeyValue" ), new QgsKeyValueWidgetFactory( tr( "Key/Value" ) ) );
registerWidget( QStringLiteral( "List" ), new QgsListWidgetFactory( tr( "List" ) ) );
registerWidget( QStringLiteral( "Binary" ), new QgsBinaryWidgetFactory( tr( "Binary (BLOB)" ) ) );
}

QgsEditorWidgetRegistry::~QgsEditorWidgetRegistry()
@@ -0,0 +1,44 @@
/***************************************************************************
qgsbinarywidgetfactory.cpp
-------------------------
Date : November 2018
Copyright : (C) 2018 Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsbinarywidgetfactory.h"

#include "qgsbinarywidgetwrapper.h"
#include "qgsdummyconfigdlg.h"
#include "qgsvectorlayer.h"
#include "qgsvectordataprovider.h"

QgsBinaryWidgetFactory::QgsBinaryWidgetFactory( const QString &name )
: QgsEditorWidgetFactory( name )
{
}

QgsEditorWidgetWrapper *QgsBinaryWidgetFactory::create( QgsVectorLayer *vl, int fieldIdx, QWidget *editor, QWidget *parent ) const
{
return new QgsBinaryWidgetWrapper( vl, fieldIdx, editor, parent );
}

QgsEditorConfigWidget *QgsBinaryWidgetFactory::configWidget( QgsVectorLayer *vl, int fieldIdx, QWidget *parent ) const
{
return new QgsDummyConfigDlg( vl, fieldIdx, parent, QObject::tr( "A widget for interacting with binary (BLOB) fields." ) );
}

unsigned int QgsBinaryWidgetFactory::fieldScore( const QgsVectorLayer *vl, int fieldIdx ) const
{
const QgsField field = vl->fields().field( fieldIdx );
const QVariant::Type type = field.type();
// ByteArray fields only
return type == QVariant::ByteArray ? 20 : 0;
}
@@ -0,0 +1,45 @@
/***************************************************************************
qgsbinarywidgetfactory.h
-----------------------
Date : November 2018
Copyright : (C) 2018 Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSBINARYWIDGETFACTORY_H
#define QGSBINARYWIDGETFACTORY_H

#include "qgseditorwidgetfactory.h"
#include "qgis_gui.h"

SIP_NO_FILE

/**
* \ingroup gui
* \class QgsBinaryWidgetFactory
* Editor widget factory for binary (BLOB) widgets.
* \note not available in Python bindings
* \since QGIS 3.6
*/

class GUI_EXPORT QgsBinaryWidgetFactory : public QgsEditorWidgetFactory
{
public:
explicit QgsBinaryWidgetFactory( const QString &name );

// QgsEditorWidgetFactory interface
public:
QgsEditorWidgetWrapper *create( QgsVectorLayer *vl, int fieldIdx, QWidget *editor, QWidget *parent ) const override;
QgsEditorConfigWidget *configWidget( QgsVectorLayer *vl, int fieldIdx, QWidget *parent ) const override;

unsigned int fieldScore( const QgsVectorLayer *vl, int fieldIdx ) const override;
};

#endif // QGSBINARYWIDGETFACTORY_H
@@ -0,0 +1,188 @@
/***************************************************************************
qgsbinarywidgetwrapper.cpp
-------------------------
Date : November 2018
Copyright : (C) 2018 Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsbinarywidgetwrapper.h"
#include "qgsvectorlayer.h"
#include "qgsvectordataprovider.h"
#include "qgsfileutils.h"
#include "qgssettings.h"
#include <QHBoxLayout>
#include <QFileDialog>
#include <QLabel>
#include <QToolButton>
#include <QAction>
#include <QMenu>
#include <QMessageBox>

QgsBinaryWidgetWrapper::QgsBinaryWidgetWrapper( QgsVectorLayer *vl, int fieldIdx, QWidget *editor, QWidget *parent )
: QgsEditorWidgetWrapper( vl, fieldIdx, editor, parent )

{
}


QVariant QgsBinaryWidgetWrapper::value() const
{
return mValue;
}

void QgsBinaryWidgetWrapper::showIndeterminateState()
{
if ( mLabel )
mLabel->clear();
}

void QgsBinaryWidgetWrapper::setEnabled( bool enabled )
{
if ( mSetAction )
mSetAction->setEnabled( enabled );
if ( mClearAction )
mClearAction->setEnabled( enabled && !mValue.isEmpty() );
}

QWidget *QgsBinaryWidgetWrapper::createWidget( QWidget *parent )
{
QWidget *container = new QWidget( parent );
QHBoxLayout *layout = new QHBoxLayout();
container->setLayout( layout );
layout->setMargin( 0 );
layout->setContentsMargins( 0, 0, 0, 0 );

QLabel *label = new QLabel();
layout->addWidget( label, 1 );

QToolButton *button = new QToolButton();
button->setText( QChar( 0x2026 ) );
layout->addWidget( button, 0 );

container->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Minimum );
return container;
}

void QgsBinaryWidgetWrapper::initWidget( QWidget *editor )
{
mLabel = editor->findChild<QLabel *>();
mButton = editor->findChild<QToolButton *>();

if ( mLabel )
{
QFont f = mLabel->font();
f.setItalic( true );
mLabel->setFont( f );
}

if ( mButton )
{
mButton->setPopupMode( QToolButton::InstantPopup );

mSetAction = new QAction( tr( "Embed File…" ), mButton );
connect( mSetAction, &QAction::triggered, this, &QgsBinaryWidgetWrapper::setContent );
mClearAction = new QAction( tr( "Clear Contents…" ), mButton );
connect( mClearAction, &QAction::triggered, this, &QgsBinaryWidgetWrapper::clear );
mSaveAction = new QAction( tr( "Save Contents to File…" ), mButton );
connect( mSaveAction, &QAction::triggered, this, &QgsBinaryWidgetWrapper::saveContent );
QMenu *menu = new QMenu( mButton );
menu->addAction( mSetAction );
menu->addAction( mClearAction );
menu->addSeparator();
menu->addAction( mSaveAction );
mButton->setMenu( menu );
}
}

bool QgsBinaryWidgetWrapper::valid() const
{
return mLabel && mButton;
}

void QgsBinaryWidgetWrapper::setValue( const QVariant &value )
{
mValue = value.toByteArray();
if ( mLabel )
{
if ( !mValue.isEmpty() )
{
mLabel->setText( tr( "Binary (%1)" ).arg( QgsFileUtils::representFileSize( mValue.size() ) ) );
}
else
{
mLabel->setText( QgsApplication::nullRepresentation() );
}
}
if ( mSaveAction )
mSaveAction->setEnabled( !mValue.isEmpty() );
if ( mClearAction )
mClearAction->setEnabled( !mValue.isEmpty() );
}

void QgsBinaryWidgetWrapper::saveContent()
{
QgsSettings s;
QString file = QFileDialog::getSaveFileName( nullptr,
tr( "Save Contents to File" ),
defaultPath(),
tr( "All files" ) + " (*.*)" );
if ( file.isEmpty() )
{
return;
}

QFileInfo fi( file );
s.setValue( QStringLiteral( "/UI/lastBinaryDir" ), fi.absolutePath() );

QFile fileOut( file );
fileOut.open( QIODevice::WriteOnly );
fileOut.write( mValue );
fileOut.close();
}

void QgsBinaryWidgetWrapper::setContent()
{
QgsSettings s;
QString file = QFileDialog::getOpenFileName( nullptr,
tr( "Embed File" ),
defaultPath(),
tr( "All files" ) + " (*.*)" );
QFileInfo fi( file );
if ( file.isEmpty() || !fi.exists() )
{
return;
}

s.setValue( QStringLiteral( "/UI/lastBinaryDir" ), fi.absolutePath() );

QFile fileSource( file );
if ( !fileSource.open( QIODevice::ReadOnly ) )
{
return;
}

setValue( fileSource.readAll() );
emitValueChanged();
}

void QgsBinaryWidgetWrapper::clear()
{
if ( QMessageBox::question( nullptr, tr( "Clear Contents" ), tr( "Are you sure you want the clear this field's content?" ), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
return;

setValue( QByteArray() );
}

QString QgsBinaryWidgetWrapper::defaultPath()
{
return QgsSettings().value( QStringLiteral( "/UI/lastBinaryDir" ), QDir::homePath() ).toString();
}

@@ -0,0 +1,73 @@
/***************************************************************************
qgsbinarywidgetwrapper.h
-----------------------
Date : November 2018
Copyright : (C) 2018 Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSBINARYWIDGETWRAPPER_H
#define QGSBINARYWIDGETWRAPPER_H

#include "qgseditorwidgetwrapper.h"
#include "qgis_gui.h"

class QLabel;
class QToolButton;

SIP_NO_FILE

/**
* \ingroup gui
* \class QgsBinaryWidgetWrapper
* Widget wrapper for binary (BLOB) fields.
* \note not available in Python bindings
* \since QGIS 3.6
*/

class GUI_EXPORT QgsBinaryWidgetWrapper : public QgsEditorWidgetWrapper
{
Q_OBJECT
public:
explicit QgsBinaryWidgetWrapper( QgsVectorLayer *vl, int fieldIdx, QWidget *editor = nullptr, QWidget *parent = nullptr );

// QgsEditorWidgetWrapper interface
public:
QVariant value() const override;
void showIndeterminateState() override;
void setEnabled( bool enabled ) override;

protected:
QWidget *createWidget( QWidget *parent ) override;
void initWidget( QWidget *editor ) override;
bool valid() const override;

public slots:
void setValue( const QVariant &value ) override;

private slots:

void saveContent();
void setContent();
void clear();

private:
QString defaultPath();

QByteArray mValue;

QLabel *mLabel = nullptr;
QToolButton *mButton = nullptr;
QAction *mSetAction = nullptr;
QAction *mClearAction = nullptr;
QAction *mSaveAction = nullptr;
};

#endif // QGSBINARYWIDGETWRAPPER_H

0 comments on commit d96ce7a

Please sign in to comment.
You can’t perform that action at this time.