Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add a QgsStatusBar widget/interface for adding messages/widgets to
main window statusbar

QStatusBar gives almost no control over display and placement
of child widgets. It's not possible to subclass and reimplement
either, due to how QMainWindow works internally, and also due to
the special handling for the size grip and other platform specific
handling in QStatusBar.

Instead, we embed a single QgsStatusBar covering the whole real
status bar. All child widgets and temporary messages instead
are pushed to the QgsStatusBar instead - giving us as much control
as we desire over how these widgets are placed and their behavior.

As a result the locator widget has been moved to its logical placement
on the left of the status bar.

All plugins must ensure that they use the status bar interface
available via iface.statusBarIface() instead of directly interacting
with the status bar (e.g. iface.mainWindow().statusBar()...)
  • Loading branch information
nyalldawson committed May 18, 2017
1 parent 3c843a8 commit 27077c8
Show file tree
Hide file tree
Showing 20 changed files with 376 additions and 44 deletions.
2 changes: 2 additions & 0 deletions doc/api_break.dox
Expand Up @@ -401,6 +401,8 @@ are similar, but only apply to composer windows when they are exist. To access a
from a project, the new QgsProject.instance().layoutManager() class should be used instead.
Additionally, the new interface methods work with QgsComposerInterface objects instead
of QgsComposerView objects.
- interaction with the main window status bar should no longer use the native Qt statusBar() method.
Instead iface.statusBarIface() should be used.

CharacterWidget {#qgis_api_break_3_0_CharacterWidget}
-------------------
Expand Down
1 change: 1 addition & 0 deletions python/gui/gui.sip
Expand Up @@ -159,6 +159,7 @@
%Include qgsshortcutsmanager.sip
%Include qgsslider.sip
%Include qgssourceselectdialog.sip
%Include qgsstatusbar.sip
%Include qgssublayersdialog.sip
%Include qgssubstitutionlistwidget.sip
%Include qgstablewidgetbase.sip
Expand Down
2 changes: 2 additions & 0 deletions python/gui/qgisinterface.sip
Expand Up @@ -509,6 +509,8 @@ class QgisInterface : QObject
/** Get timeout for timed messages: default of 5 seconds */
virtual int messageTimeout() = 0;

virtual QgsStatusBar *statusBarIface() = 0;

/**
* Registers a locator \a filter for the app's locator bar. Ownership of the filter is transferred to the
* locator.
Expand Down
98 changes: 98 additions & 0 deletions python/gui/qgsstatusbar.sip
@@ -0,0 +1,98 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/qgsstatusbar.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/





class QgsStatusBar : QWidget
{
%Docstring
A proxy widget for QStatusBar.

Unlike QStatusBar, QgsStatusBar allows finer control of widget placement, including
the option to locate permanent widgets on the left side of the bar.

QgsStatusBar is designed to be embedded into an existing
window's QStatusBar, as a permanent widget. This allows reuse of the special QStatusBar handling
for resize grips and other platform specific status bar tweaks.

Instead of adding child widgets and showing messages directly in the window's status bar,
these widgets (and messages) should instead be added into the embedded QgsStatusBar.

.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgsstatusbar.h"
%End
public:

enum Anchor
{
AnchorLeft,
AnchorRight,
};

QgsStatusBar( QWidget *parent /TransferThis/ = 0 );
%Docstring
Constructor for QgsStatusBar.
%End

void addPermanentWidget( QWidget *widget /Transfer/, int stretch = 0, Anchor anchor = AnchorRight );
%Docstring
Adds the given ``widget`` permanently to this status bar, reparenting the widget if it isn't already a child
of this object.

The ``stretch`` parameter is used to compute a suitable size for the given widget as the status bar
grows and shrinks. The default stretch factor is 0, i.e giving the widget a minimum of space.

The ``anchor`` parameter controls which side of the status bar the widget should be anchored to.
%End

void removeWidget( QWidget *widget );
%Docstring
Removes a ``widget`` from the status bar. Ownership of the widget remains unchanged, and the
widget itself is not deleted.
%End

QString currentMessage() const;
%Docstring
Returns the current message shown in the status bar.
.. seealso:: showMessage()
:rtype: str
%End

public slots:

void showMessage( const QString &message, int timeout = 0 );
%Docstring
Displays the given ``message`` for the specified number of milli-seconds (``timeout``).
If ``timeout`` is 0 (default), the message remains displayed until the clearMessage()
slot is called or until the showMessage() slot is called again to change the message.
.. seealso:: clearMessage()
.. seealso:: currentMessage()
%End

void clearMessage();
%Docstring
Removes any temporary message being shown.
.. seealso:: showMessage()
%End

};



/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/qgsstatusbar.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
3 changes: 2 additions & 1 deletion src/app/gps/qgsgpsinformationwidget.cpp
Expand Up @@ -36,6 +36,7 @@
#include "qgsvectorlayer.h"
#include "qgswkbptr.h"
#include "qgssettings.h"
#include "qgsstatusbar.h"

// QWT Charting widget

Expand Down Expand Up @@ -1122,5 +1123,5 @@ void QgsGPSInformationWidget::setStatusIndicator( const FixStatus statusValue )

void QgsGPSInformationWidget::showStatusBarMessage( const QString &msg )
{
QgisApp::instance()->statusBar()->showMessage( msg );
QgisApp::instance()->statusBarIface()->showMessage( msg );
}
6 changes: 3 additions & 3 deletions src/app/nodetool/qgsnodetool.cpp
Expand Up @@ -31,7 +31,7 @@
#include "qgssnappingutils.h"
#include "qgsvectorlayer.h"
#include "qgsvertexmarker.h"

#include "qgsstatusbar.h"
#include "qgisapp.h"
#include "qgsselectedfeature.h"
#include "qgsnodeeditor.h"
Expand Down Expand Up @@ -1797,7 +1797,7 @@ void QgsNodeTool::validationFinished()
GeometryValidation &validation = *it;
if ( validation.validator == validator )
{
QStatusBar *sb = QgisApp::instance()->statusBar();
QgsStatusBar *sb = QgisApp::instance()->statusBarIface();
sb->showMessage( tr( "Validation finished (%n error(s) found).", "number of geometry errors", validation.errorMarkers.size() ) );
if ( validation.errorMarkers.isEmpty() )
{
Expand Down Expand Up @@ -1838,7 +1838,7 @@ void QgsNodeTool::GeometryValidation::addError( QgsGeometry::Error e )
errorMarkers << marker;
}

QStatusBar *sb = QgisApp::instance()->statusBar();
QgsStatusBar *sb = QgisApp::instance()->statusBarIface();
sb->showMessage( e.what() );
sb->setToolTip( errors );
}
Expand Down
7 changes: 4 additions & 3 deletions src/app/nodetool/qgsselectedfeature.cpp
Expand Up @@ -27,6 +27,7 @@
#include "qgisapp.h"
#include "qgslayertreeview.h"
#include "qgsproject.h"
#include "qgsstatusbar.h"

QgsSelectedFeature::QgsSelectedFeature( QgsFeatureId featureId,
QgsVectorLayer *vlayer,
Expand Down Expand Up @@ -189,7 +190,7 @@ void QgsSelectedFeature::validateGeometry( QgsGeometry *g )
connect( mValidator, &QThread::finished, this, &QgsSelectedFeature::validationFinished );
mValidator->start();

QStatusBar *sb = QgisApp::instance()->statusBar();
QgsStatusBar *sb = QgisApp::instance()->statusBarIface();
sb->showMessage( tr( "Validation started." ) );
}

Expand All @@ -212,14 +213,14 @@ void QgsSelectedFeature::addError( QgsGeometry::Error e )
mGeomErrorMarkers << marker;
}

QStatusBar *sb = QgisApp::instance()->statusBar();
QgsStatusBar *sb = QgisApp::instance()->statusBarIface();
sb->showMessage( e.what() );
sb->setToolTip( mTip );
}

void QgsSelectedFeature::validationFinished()
{
QStatusBar *sb = QgisApp::instance()->statusBar();
QgsStatusBar *sb = QgisApp::instance()->statusBarIface();
sb->showMessage( tr( "Validation finished (%n error(s) found).", "number of geometry errors", mGeomErrorMarkers.size() ) );
}

Expand Down
63 changes: 34 additions & 29 deletions src/app/qgisapp.cpp
Expand Up @@ -248,6 +248,7 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
#include "qgssourceselectdialog.h"
#include "qgssponsors.h"
#include "qgsstatisticalsummarydockwidget.h"
#include "qgsstatusbar.h"
#include "qgsstatusbarcoordinateswidget.h"
#include "qgsstatusbarmagnifierwidget.h"
#include "qgsstatusbarscalewidget.h"
Expand Down Expand Up @@ -2521,21 +2522,25 @@ void QgisApp::createStatusBar()
//remove borders from children under Windows
statusBar()->setStyleSheet( QStringLiteral( "QStatusBar::item {border: none;}" ) );

mStatusBar = new QgsStatusBar();

statusBar()->addPermanentWidget( mStatusBar, 10 );

// Add a panel to the status bar for the scale, coords and progress
// And also rendering suppression checkbox
mProgressBar = new QProgressBar( statusBar() );
mProgressBar = new QProgressBar( mStatusBar );
mProgressBar->setObjectName( QStringLiteral( "mProgressBar" ) );
mProgressBar->setMaximumWidth( 100 );
mProgressBar->hide();
mProgressBar->setWhatsThis( tr( "Progress bar that displays the status "
"of rendering layers and other time-intensive operations" ) );
statusBar()->addPermanentWidget( mProgressBar, 1 );
mStatusBar->addPermanentWidget( mProgressBar, 1 );

connect( mMapCanvas, &QgsMapCanvas::renderStarting, this, &QgisApp::canvasRefreshStarted );
connect( mMapCanvas, &QgsMapCanvas::mapCanvasRefreshed, this, &QgisApp::canvasRefreshFinished );

mTaskManagerWidget = new QgsTaskManagerStatusBarWidget( QgsApplication::taskManager(), statusBar() );
statusBar()->addPermanentWidget( mTaskManagerWidget, 0 );
mTaskManagerWidget = new QgsTaskManagerStatusBarWidget( QgsApplication::taskManager(), mStatusBar );
mStatusBar->addPermanentWidget( mTaskManagerWidget, 0 );

// Bumped the font up one point size since 8 was too
// small on some platforms. A point size of 9 still provides
Expand All @@ -2544,29 +2549,29 @@ void QgisApp::createStatusBar()
statusBar()->setFont( myFont );

//coords status bar widget
mCoordsEdit = new QgsStatusBarCoordinatesWidget( statusBar() );
mCoordsEdit = new QgsStatusBarCoordinatesWidget( mStatusBar );
mCoordsEdit->setObjectName( QStringLiteral( "mCoordsEdit" ) );
mCoordsEdit->setMapCanvas( mMapCanvas );
mCoordsEdit->setFont( myFont );
statusBar()->addPermanentWidget( mCoordsEdit, 0 );
mStatusBar->addPermanentWidget( mCoordsEdit, 0 );

mScaleWidget = new QgsStatusBarScaleWidget( mMapCanvas, statusBar() );
mScaleWidget = new QgsStatusBarScaleWidget( mMapCanvas, mStatusBar );
mScaleWidget->setObjectName( QStringLiteral( "mScaleWidget" ) );
mScaleWidget->setFont( myFont );
connect( mScaleWidget, &QgsStatusBarScaleWidget::scaleLockChanged, mMapCanvas, &QgsMapCanvas::setScaleLocked );
statusBar()->addPermanentWidget( mScaleWidget, 0 );
mStatusBar->addPermanentWidget( mScaleWidget, 0 );

// zoom widget
mMagnifierWidget = new QgsStatusBarMagnifierWidget( statusBar() );
mMagnifierWidget = new QgsStatusBarMagnifierWidget( mStatusBar );
mMagnifierWidget->setObjectName( QStringLiteral( "mMagnifierWidget" ) );
mMagnifierWidget->setFont( myFont );
connect( mMapCanvas, &QgsMapCanvas::magnificationChanged, mMagnifierWidget, &QgsStatusBarMagnifierWidget::updateMagnification );
connect( mMagnifierWidget, &QgsStatusBarMagnifierWidget::magnificationChanged, mMapCanvas, &QgsMapCanvas::setMagnificationFactor );
mMagnifierWidget->updateMagnification( QSettings().value( QStringLiteral( "/qgis/magnifier_factor_default" ), 1.0 ).toDouble() );
statusBar()->addPermanentWidget( mMagnifierWidget, 0 );
mStatusBar->addPermanentWidget( mMagnifierWidget, 0 );

// add a widget to show/set current rotation
mRotationLabel = new QLabel( QString(), statusBar() );
mRotationLabel = new QLabel( QString(), mStatusBar );
mRotationLabel->setObjectName( QStringLiteral( "mRotationLabel" ) );
mRotationLabel->setFont( myFont );
mRotationLabel->setMinimumWidth( 10 );
Expand All @@ -2576,9 +2581,9 @@ void QgisApp::createStatusBar()
mRotationLabel->setFrameStyle( QFrame::NoFrame );
mRotationLabel->setText( tr( "Rotation" ) );
mRotationLabel->setToolTip( tr( "Current clockwise map rotation in degrees" ) );
statusBar()->addPermanentWidget( mRotationLabel, 0 );
mStatusBar->addPermanentWidget( mRotationLabel, 0 );

mRotationEdit = new QgsDoubleSpinBox( statusBar() );
mRotationEdit = new QgsDoubleSpinBox( mStatusBar );
mRotationEdit->setObjectName( QStringLiteral( "mRotationEdit" ) );
mRotationEdit->setClearValue( 0.0 );
mRotationEdit->setKeyboardTracking( false );
Expand All @@ -2593,13 +2598,13 @@ void QgisApp::createStatusBar()
"in degrees. It also allows editing to set "
"the rotation" ) );
mRotationEdit->setToolTip( tr( "Current clockwise map rotation in degrees" ) );
statusBar()->addPermanentWidget( mRotationEdit, 0 );
mStatusBar->addPermanentWidget( mRotationEdit, 0 );
connect( mRotationEdit, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, &QgisApp::userRotation );

showRotation();

// render suppression status bar widget
mRenderSuppressionCBox = new QCheckBox( tr( "Render" ), statusBar() );
mRenderSuppressionCBox = new QCheckBox( tr( "Render" ), mStatusBar );
mRenderSuppressionCBox->setObjectName( QStringLiteral( "mRenderSuppressionCBox" ) );
mRenderSuppressionCBox->setChecked( true );
mRenderSuppressionCBox->setFont( myFont );
Expand All @@ -2608,11 +2613,11 @@ void QgisApp::createStatusBar()
"events. When not checked, no rendering is done. This allows you "
"to add a large number of layers and symbolize them before rendering." ) );
mRenderSuppressionCBox->setToolTip( tr( "Toggle map rendering" ) );
statusBar()->addPermanentWidget( mRenderSuppressionCBox, 0 );
mStatusBar->addPermanentWidget( mRenderSuppressionCBox, 0 );
// On the fly projection status bar icon
// Changed this to a tool button since a QPushButton is
// sculpted on OS X and the icon is never displayed [gsherman]
mOnTheFlyProjectionStatusButton = new QToolButton( statusBar() );
mOnTheFlyProjectionStatusButton = new QToolButton( mStatusBar );
mOnTheFlyProjectionStatusButton->setAutoRaise( true );
mOnTheFlyProjectionStatusButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
mOnTheFlyProjectionStatusButton->setObjectName( QStringLiteral( "mOntheFlyProjectionStatusButton" ) );
Expand All @@ -2628,21 +2633,21 @@ void QgisApp::createStatusBar()
"to open coordinate reference system dialog" ) );
connect( mOnTheFlyProjectionStatusButton, &QAbstractButton::clicked,
this, &QgisApp::projectPropertiesProjections );//bring up the project props dialog when clicked
statusBar()->addPermanentWidget( mOnTheFlyProjectionStatusButton, 0 );
statusBar()->showMessage( tr( "Ready" ) );
mStatusBar->addPermanentWidget( mOnTheFlyProjectionStatusButton, 0 );
mStatusBar->showMessage( tr( "Ready" ) );

mMessageButton = new QToolButton( statusBar() );
mMessageButton = new QToolButton( mStatusBar );
mMessageButton->setAutoRaise( true );
mMessageButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mMessageLogRead.svg" ) ) );
mMessageButton->setToolTip( tr( "Messages" ) );
mMessageButton->setWhatsThis( tr( "Messages" ) );
mMessageButton->setObjectName( QStringLiteral( "mMessageLogViewerButton" ) );
mMessageButton->setMaximumHeight( mScaleWidget->height() );
mMessageButton->setCheckable( true );
statusBar()->addPermanentWidget( mMessageButton, 0 );
mStatusBar->addPermanentWidget( mMessageButton, 0 );

mLocatorWidget = new QgsLocatorWidget( statusBar() );
statusBar()->addPermanentWidget( mLocatorWidget );
mLocatorWidget = new QgsLocatorWidget( mStatusBar );
mStatusBar->addPermanentWidget( mLocatorWidget, 0, QgsStatusBar::AnchorLeft );
QShortcut *locatorShortCut = new QShortcut( QKeySequence( tr( "Ctrl+K" ) ), this );
connect( locatorShortCut, &QShortcut::activated, mLocatorWidget, [ = ] { mLocatorWidget->search( QString() ); } );
locatorShortCut->setObjectName( QStringLiteral( "Locator" ) );
Expand Down Expand Up @@ -5222,7 +5227,7 @@ void QgisApp::enableProjectMacros()
bool QgisApp::addProject( const QString &projectFile )
{
QFileInfo pfi( projectFile );
statusBar()->showMessage( tr( "Loading project: %1" ).arg( pfi.fileName() ) );
mStatusBar->showMessage( tr( "Loading project: %1" ).arg( pfi.fileName() ) );
qApp->processEvents();

QApplication::setOverrideCursor( Qt::WaitCursor );
Expand All @@ -5249,7 +5254,7 @@ bool QgisApp::addProject( const QString &projectFile )
buttons |= QMessageBox::Ok;
}
QApplication::restoreOverrideCursor();
statusBar()->clearMessage();
mStatusBar->clearMessage();

int r = QMessageBox::critical( this,
tr( "Unable to open project" ),
Expand Down Expand Up @@ -5355,7 +5360,7 @@ bool QgisApp::addProject( const QString &projectFile )
mMapCanvas->freeze( false );
mMapCanvas->refresh();

statusBar()->showMessage( tr( "Project loaded" ), 3000 );
mStatusBar->showMessage( tr( "Project loaded" ), 3000 );
return true;
} // QgisApp::addProject(QString projectFile)

Expand Down Expand Up @@ -5421,7 +5426,7 @@ bool QgisApp::fileSave()
if ( QgsProject::instance()->write() )
{
setTitleBarText_( *this ); // update title bar
statusBar()->showMessage( tr( "Saved project to: %1" ).arg( QDir::toNativeSeparators( QgsProject::instance()->fileName() ) ), 5000 );
mStatusBar->showMessage( tr( "Saved project to: %1" ).arg( QDir::toNativeSeparators( QgsProject::instance()->fileName() ) ), 5000 );

saveRecentProjectPath( fullPath.filePath() );

Expand Down Expand Up @@ -5473,7 +5478,7 @@ void QgisApp::fileSaveAs()
if ( QgsProject::instance()->write() )
{
setTitleBarText_( *this ); // update title bar
statusBar()->showMessage( tr( "Saved project to: %1" ).arg( QDir::toNativeSeparators( QgsProject::instance()->fileName() ) ), 5000 );
mStatusBar->showMessage( tr( "Saved project to: %1" ).arg( QDir::toNativeSeparators( QgsProject::instance()->fileName() ) ), 5000 );
// add this to the list of recently used project files
saveRecentProjectPath( fullPath.filePath() );
mProjectLastModified = fullPath.lastModified();
Expand Down Expand Up @@ -10885,7 +10890,7 @@ void QgisApp::updateMouseCoordinatePrecision()

void QgisApp::showStatusMessage( const QString &message )
{
statusBar()->showMessage( message );
mStatusBar->showMessage( message );
}

void QgisApp::displayMapToolMessage( const QString &message, QgsMessageBar::MessageLevel level )
Expand Down

0 comments on commit 27077c8

Please sign in to comment.