Skip to content

Commit

Permalink
[FEATURE] New form widget for binary (blob) fields
Browse files Browse the repository at this point in the history
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 12, 2018
1 parent 608d03c commit d96ce7a
Show file tree
Hide file tree
Showing 6 changed files with 356 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/gui/CMakeLists.txt
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/gui/editorwidgets/core/qgseditorwidgetregistry.cpp
Expand Up @@ -23,6 +23,7 @@
#include "qgssearchwidgetwrapper.h"

// Editors
#include "qgsbinarywidgetfactory.h"
#include "qgsclassificationwidgetwrapperfactory.h"
#include "qgscheckboxwidgetfactory.h"
#include "qgscolorwidgetfactory.h"
Expand Down Expand Up @@ -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()
Expand Down
44 changes: 44 additions & 0 deletions src/gui/editorwidgets/qgsbinarywidgetfactory.cpp
@@ -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;
}
45 changes: 45 additions & 0 deletions src/gui/editorwidgets/qgsbinarywidgetfactory.h
@@ -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
188 changes: 188 additions & 0 deletions src/gui/editorwidgets/qgsbinarywidgetwrapper.cpp
@@ -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();
}

73 changes: 73 additions & 0 deletions src/gui/editorwidgets/qgsbinarywidgetwrapper.h
@@ -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.