QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgsmessagebar.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmessagebar.cpp - description
3  -------------------
4  begin : June 2012
5  copyright : (C) 2012 by Giuseppe Sucameli
6  email : sucameli at faunalia dot it
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgsmessagebar.h"
19 #include "qgsmessagebaritem.h"
20 #include "qgsapplication.h"
21 #include "qgsmessagelog.h"
22 #include "qgsmessageviewer.h"
23 
24 #include <QWidget>
25 #include <QPalette>
26 #include <QStackedWidget>
27 #include <QProgressBar>
28 #include <QToolButton>
29 #include <QTimer>
30 #include <QGridLayout>
31 #include <QMenu>
32 #include <QMouseEvent>
33 #include <QLabel>
34 
35 QgsMessageBar::QgsMessageBar( QWidget *parent )
36  : QFrame( parent )
37 
38 {
39  QPalette pal = palette();
40  pal.setBrush( backgroundRole(), pal.window() );
41  setPalette( pal );
42  setAutoFillBackground( true );
43  setFrameShape( QFrame::StyledPanel );
44  setFrameShadow( QFrame::Plain );
45 
46  mLayout = new QGridLayout( this );
47  const int xMargin = std::max( 9.0, Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 0.45 );
48  const int yMargin = std::max( 1.0, Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 0.05 );
49  mLayout->setContentsMargins( xMargin, yMargin, xMargin, yMargin );
50  setLayout( mLayout );
51 
52  mCountProgress = new QProgressBar( this );
53  mCountStyleSheet = QString( "QProgressBar { border: 1px solid rgba(0, 0, 0, 75%);"
54  " border-radius: 2px; background: rgba(0, 0, 0, 0);"
55  " image: url(:/images/themes/default/%1) }"
56  "QProgressBar::chunk { background-color: rgba(0, 0, 0, 30%); width: 5px; }" );
57 
58  mCountProgress->setStyleSheet( mCountStyleSheet.arg( QLatin1String( "mIconTimerPause.svg" ) ) );
59  mCountProgress->setObjectName( QStringLiteral( "mCountdown" ) );
60  const int barWidth = std::max( 25.0, Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 1.25 );
61  const int barHeight = std::max( 14.0, Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 0.7 );
62  mCountProgress->setFixedSize( barWidth, barHeight );
63  mCountProgress->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
64  mCountProgress->setTextVisible( false );
65  mCountProgress->setRange( 0, 5 );
66  mCountProgress->setHidden( true );
67  mLayout->addWidget( mCountProgress, 0, 0, 1, 1 );
68 
69  mItemCount = new QLabel( this );
70  mItemCount->setObjectName( QStringLiteral( "mItemCount" ) );
71  mItemCount->setToolTip( tr( "Remaining messages" ) );
72  mItemCount->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Preferred );
73  mLayout->addWidget( mItemCount, 0, 2, 1, 1 );
74 
75  mCloseMenu = new QMenu( this );
76  mCloseMenu->setObjectName( QStringLiteral( "mCloseMenu" ) );
77  mActionCloseAll = new QAction( tr( "Close All" ), this );
78  mCloseMenu->addAction( mActionCloseAll );
79  connect( mActionCloseAll, &QAction::triggered, this, &QgsMessageBar::clearWidgets );
80 
81  mCloseBtn = new QToolButton( this );
82  mCloseMenu->setObjectName( QStringLiteral( "mCloseMenu" ) );
83  mCloseBtn->setToolTip( tr( "Close" ) );
84  mCloseBtn->setMinimumWidth( QgsGuiUtils::scaleIconSize( 44 ) );
85  mCloseBtn->setStyleSheet(
86  "QToolButton { border:none; background-color: rgba(0, 0, 0, 0); }"
87  "QToolButton::menu-button { border:none; background-color: rgba(0, 0, 0, 0); }" );
88  mCloseBtn->setCursor( Qt::PointingHandCursor );
89  mCloseBtn->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconClose.svg" ) ) );
90 
91  const int iconSize = std::max( 18.0, Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 0.9 );
92  mCloseBtn->setIconSize( QSize( iconSize, iconSize ) );
93  mCloseBtn->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum );
94  mCloseBtn->setMenu( mCloseMenu );
95  mCloseBtn->setPopupMode( QToolButton::MenuButtonPopup );
96  connect( mCloseBtn, &QAbstractButton::clicked, this, static_cast < bool ( QgsMessageBar::* )() > ( &QgsMessageBar::popWidget ) );
97  mLayout->addWidget( mCloseBtn, 0, 3, 1, 1 );
98 
99  mCountdownTimer = new QTimer( this );
100  mCountdownTimer->setInterval( 1000 );
101  connect( mCountdownTimer, &QTimer::timeout, this, &QgsMessageBar::updateCountdown );
102 
103  connect( this, &QgsMessageBar::widgetAdded, this, &QgsMessageBar::updateItemCount );
104  connect( this, &QgsMessageBar::widgetRemoved, this, &QgsMessageBar::updateItemCount );
105 
106  // start hidden
107  setVisible( false );
108 }
109 
110 void QgsMessageBar::mousePressEvent( QMouseEvent *e )
111 {
112  if ( mCountProgress == childAt( e->pos() ) && e->button() == Qt::LeftButton )
113  {
114  if ( mCountdownTimer->isActive() )
115  {
116  mCountdownTimer->stop();
117  mCountProgress->setStyleSheet( mCountStyleSheet.arg( QLatin1String( "mIconTimerContinue.svg" ) ) );
118  }
119  else
120  {
121  mCountdownTimer->start();
122  mCountProgress->setStyleSheet( mCountStyleSheet.arg( QLatin1String( "mIconTimerPause.svg" ) ) );
123  }
124  }
125 }
126 
127 void QgsMessageBar::popItem( QgsMessageBarItem *item )
128 {
129  Q_ASSERT( item );
130 
131  if ( !mItems.contains( item ) )
132  return;
133 
134  if ( item == mItems.at( 0 ) )
135  {
136  mItems.removeOne( item );
137  mLayout->removeWidget( item );
138  item->hide();
139  disconnect( item, &QgsMessageBarItem::styleChanged, this, &QWidget::setStyleSheet );
140  item->deleteLater();
141 
142  if ( !mItems.isEmpty() )
143  {
144  showItem( mItems.at( 0 ) );
145  }
146  else
147  {
148  hide();
149  }
150  }
151  else
152  {
153  mItems.removeOne( item );
154  item->deleteLater();
155  }
156 
157  emit widgetRemoved( item );
158 }
159 
161 {
162  if ( !item || !mItems.contains( item ) )
163  return false;
164 
165  popItem( item );
166  return true;
167 }
168 
170 {
171  if ( mItems.empty() )
172  return false;
173 
174  resetCountdown();
175 
176  popItem( mItems.at( 0 ) );
177 
178  return true;
179 }
180 
182 {
183  if ( mItems.empty() )
184  return true;
185 
186  while ( !mItems.isEmpty() )
187  {
188  popWidget();
189  }
190 
191  return true;
192 }
193 
194 void QgsMessageBar::pushSuccess( const QString &title, const QString &message )
195 {
196  pushMessage( title, message, Qgis::Success );
197 }
198 
199 void QgsMessageBar::pushInfo( const QString &title, const QString &message )
200 {
201  pushMessage( title, message, Qgis::Info );
202 }
203 
204 void QgsMessageBar::pushWarning( const QString &title, const QString &message )
205 {
206  pushMessage( title, message, Qgis::Warning );
207 }
208 
209 void QgsMessageBar::pushCritical( const QString &title, const QString &message )
210 {
211  pushMessage( title, message, Qgis::Critical );
212 }
213 
214 void QgsMessageBar::showItem( QgsMessageBarItem *item )
215 {
216  Q_ASSERT( item );
217 
218  if ( !mItems.empty() )
219  disconnect( mItems.at( 0 ), &QgsMessageBarItem::styleChanged, this, &QWidget::setStyleSheet );
220 
221  if ( mItems.count() >= MAX_ITEMS )
222  removeLowestPriorityOldestItem();
223 
224  if ( !mItems.empty() )
225  {
226  mLayout->removeWidget( mItems.at( 0 ) );
227  mItems.at( 0 )->hide();
228  }
229 
230  if ( mItems.contains( item ) )
231  mItems.removeOne( item );
232  mItems.prepend( item );
233 
234  mLayout->addWidget( item, 0, 1, 1, 1 );
235  item->show();
236 
237  if ( item->duration() > 0 )
238  {
239  mCountProgress->setRange( 0, item->duration() );
240  mCountProgress->setValue( item->duration() );
241  mCountProgress->setVisible( true );
242  mCountdownTimer->start();
243  }
244 
245  connect( item, &QgsMessageBarItem::styleChanged, this, &QWidget::setStyleSheet );
246 
247  if ( item->level() != mPrevLevel )
248  {
249  setStyleSheet( item->getStyleSheet() );
250  mPrevLevel = item->level();
251  }
252 
253  show();
254 
255  emit widgetAdded( item );
256 }
257 
258 void QgsMessageBar::removeLowestPriorityOldestItem()
259 {
261  {
262  for ( int i = mItems.size() - 1; i >= 0; --i )
263  {
264  QgsMessageBarItem *item = mItems.at( i );
265  if ( item->level() == level )
266  {
267  popItem( item );
268  return;
269  }
270  }
271  }
272 }
273 
275 {
276  resetCountdown();
277 
278  item->mMessageBar = this;
279 
280  // avoid duplicated widget
281  popWidget( item );
282  showItem( item );
283 
284  // Log all (non-empty) messages that are sent to the message bar into the message log so the
285  // user can get them back easier.
286  QString formattedTitle;
287  if ( !item->title().isEmpty() && !item->text().isEmpty() )
288  formattedTitle = QStringLiteral( "%1 : %2" ).arg( item->title(), item->text() );
289  else if ( !item->title().isEmpty() )
290  formattedTitle = item->title();
291  else if ( !item->text().isEmpty() )
292  formattedTitle = item->text();
293 
294  if ( !formattedTitle.isEmpty() )
295  QgsMessageLog::logMessage( formattedTitle, tr( "Messages" ), item->level() );
296 }
297 
298 QgsMessageBarItem *QgsMessageBar::pushWidget( QWidget *widget, Qgis::MessageLevel level, int duration )
299 {
300  QgsMessageBarItem *item = nullptr;
301  item = dynamic_cast<QgsMessageBarItem *>( widget );
302  if ( item )
303  {
304  item->setLevel( level )->setDuration( duration );
305  }
306  else
307  {
308  item = new QgsMessageBarItem( widget, level, duration );
309  }
310  pushItem( item );
311  return item;
312 }
313 
314 void QgsMessageBar::pushMessage( const QString &title, const QString &text, Qgis::MessageLevel level, int duration )
315 {
316  // block duplicate items, avoids flooding (and freezing) of the main window
317  for ( auto it = mItems.constBegin(); it != mItems.constEnd(); ++it )
318  {
319  if ( level == ( *it )->level() && title == ( *it )->title() && text == ( *it )->text() )
320  return;
321  }
322 
323  QgsMessageBarItem *item = new QgsMessageBarItem( title, text, level, duration );
324  pushItem( item );
325 }
326 
327 void QgsMessageBar::pushMessage( const QString &title, const QString &text, const QString &showMore, Qgis::MessageLevel level, int duration )
328 {
330  mv->setWindowTitle( title );
331  mv->setMessageAsPlainText( text + "\n\n" + showMore );
332 
333  QToolButton *showMoreButton = new QToolButton();
334  QAction *act = new QAction( showMoreButton );
335  act->setText( tr( "Show more" ) );
336  showMoreButton->setStyleSheet( QStringLiteral( "background-color: rgba(255, 255, 255, 0); color: black; text-decoration: underline;" ) );
337  showMoreButton->setCursor( Qt::PointingHandCursor );
338  showMoreButton->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Preferred );
339  showMoreButton->addAction( act );
340  showMoreButton->setDefaultAction( act );
341  connect( showMoreButton, &QToolButton::triggered, mv, &QDialog::exec );
342  connect( showMoreButton, &QToolButton::triggered, showMoreButton, &QObject::deleteLater );
343 
345  title,
346  text,
347  showMoreButton,
348  level,
349  duration );
350  pushItem( item );
351 }
352 
354 {
355  return mItems.value( 0 );
356 }
357 
358 QList<QgsMessageBarItem *> QgsMessageBar::items()
359 {
360  return mItems;
361 }
362 
363 QgsMessageBarItem *QgsMessageBar::createMessage( const QString &text, QWidget *parent )
364 {
365  QgsMessageBarItem *item = new QgsMessageBarItem( text, Qgis::Info, 0, parent );
366  return item;
367 }
368 
369 QgsMessageBarItem *QgsMessageBar::createMessage( const QString &title, const QString &text, QWidget *parent )
370 {
371  return new QgsMessageBarItem( title, text, Qgis::Info, 0, parent );
372 }
373 
374 QgsMessageBarItem *QgsMessageBar::createMessage( QWidget *widget, QWidget *parent )
375 {
376  return new QgsMessageBarItem( widget, Qgis::Info, 0, parent );
377 }
378 
379 void QgsMessageBar::pushMessage( const QString &text, Qgis::MessageLevel level, int duration )
380 {
381  pushMessage( QString(), text, level, duration );
382 }
383 
384 void QgsMessageBar::updateCountdown()
385 {
386  if ( !mCountdownTimer->isActive() )
387  {
388  resetCountdown();
389  return;
390  }
391  if ( mCountProgress->value() < 2 )
392  {
393  popWidget();
394  }
395  else
396  {
397  mCountProgress->setValue( mCountProgress->value() - 1 );
398  }
399 }
400 
401 void QgsMessageBar::resetCountdown()
402 {
403  if ( mCountdownTimer->isActive() )
404  mCountdownTimer->stop();
405 
406  mCountProgress->setStyleSheet( mCountStyleSheet.arg( QLatin1String( "mIconTimerPause.svg" ) ) );
407  mCountProgress->setVisible( false );
408 }
409 
410 void QgsMessageBar::updateItemCount()
411 {
412  const bool moreMessages = mItems.count() > 1;
413  mItemCount->setText( moreMessages ? tr( "%n more", "unread messages", mItems.count() - 1 ) : QString() );
414 
415  // do not show the down arrow for opening menu with "close all" if there is just one message
416  mCloseBtn->setMenu( moreMessages ? mCloseMenu : nullptr );
417  mCloseBtn->setPopupMode( moreMessages ? QToolButton::MenuButtonPopup : QToolButton::DelayedPopup );
418 }
QgsMessageBar::widgetAdded
void widgetAdded(QgsMessageBarItem *item)
Emitted whenever an item is added to the bar.
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Definition: qgsapplication.cpp:626
qgsmessagebaritem.h
QgsMessageBar::pushWidget
QgsMessageBarItem * pushWidget(QWidget *widget, Qgis::MessageLevel level=Qgis::Info, int duration=0)
Display a widget as a message on the bar, after hiding the currently visible one and putting it in a ...
Definition: qgsmessagebar.cpp:298
QgsMessageBar::pushCritical
void pushCritical(const QString &title, const QString &message)
Pushes a critical warning message with default timeout to the message bar.
Definition: qgsmessagebar.cpp:209
QgsMessageBarItem::title
QString title() const
Returns the title for the message.
Definition: qgsmessagebaritem.cpp:216
QgsMessageViewer::setMessageAsPlainText
void setMessageAsPlainText(const QString &msg)
Definition: qgsmessageviewer.cpp:46
Qgis::Warning
@ Warning
Definition: qgis.h:91
QgsMessageBar::QgsMessageBar
QgsMessageBar(QWidget *parent=nullptr)
Constructor for QgsMessageBar.
Definition: qgsmessagebar.cpp:35
Qgis::Success
@ Success
Definition: qgis.h:93
QgsMessageBarItem::level
Qgis::MessageLevel level() const
Returns the message level for the message.
Definition: qgsmessagebaritem.cpp:233
pal
Definition: qgsdiagramrenderer.h:49
QgsGuiUtils::iconSize
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
Definition: qgsguiutils.cpp:250
qgsapplication.h
QgsMessageBarItem::duration
int duration() const
Returns the duration (in seconds) of the message.
Definition: qgsmessagebaritem.h:175
QgsMessageBar::clearWidgets
bool clearWidgets()
Removes all items from the bar.
Definition: qgsmessagebar.cpp:181
Qgis::Info
@ Info
Definition: qgis.h:90
QgsMessageBarItem
Represents an item shown within a QgsMessageBar widget.
Definition: qgsmessagebaritem.h:39
QgsMessageBar::pushItem
void pushItem(QgsMessageBarItem *item)
Display a message item on the bar, after hiding the currently visible one and putting it in a stack.
Definition: qgsmessagebar.cpp:274
qgsmessageviewer.h
QgsMessageBarItem::getStyleSheet
QString getStyleSheet()
Returns the styleSheet which should be used to style a QgsMessageBar object when this item is display...
Definition: qgsmessagebaritem.h:181
QgsMessageBar
A bar for displaying non-blocking messages to the user.
Definition: qgsmessagebar.h:61
QgsMessageBar::currentItem
QgsMessageBarItem * currentItem()
Returns the current visible item, or nullptr if no item is shown.
Definition: qgsmessagebar.cpp:353
Qgis::UI_SCALE_FACTOR
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:182
qgsmessagebar.h
QgsMessageBarItem::text
QString text() const
Returns the text for the message.
Definition: qgsmessagebaritem.cpp:204
QgsMessageViewer
A generic message view for displaying QGIS messages.
Definition: qgsmessageviewer.h:33
QgsMessageBar::items
QList< QgsMessageBarItem * > items()
Returns a list of all items currently visible or queued for the bar.
Definition: qgsmessagebar.cpp:358
QgsMessageBar::mousePressEvent
void mousePressEvent(QMouseEvent *e) override
Definition: qgsmessagebar.cpp:110
QgsMessageBar::popWidget
bool popWidget()
Remove the currently displayed item from the bar and display the next item in the stack.
Definition: qgsmessagebar.cpp:169
Qgis::MessageLevel
MessageLevel
Level for messages This will be used both for message log and message bar in application.
Definition: qgis.h:89
QgsMessageBar::pushSuccess
void pushSuccess(const QString &title, const QString &message)
Pushes a success message with default timeout to the message bar.
Definition: qgsmessagebar.cpp:194
QgsMessageBarItem::styleChanged
void styleChanged(const QString &styleSheet)
Emitted when the item's message level has changed and the message bar style will need to be updated a...
QgsMessageLog::logMessage
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Definition: qgsmessagelog.cpp:27
QgsMessageBar::pushWarning
void pushWarning(const QString &title, const QString &message)
Pushes a warning message with default timeout to the message bar.
Definition: qgsmessagebar.cpp:204
QgsMessageBarItem::setDuration
QgsMessageBarItem * setDuration(int duration)
Sets the duration (in seconds) to show the message for.
Definition: qgsmessagebaritem.cpp:271
Qgis::Critical
@ Critical
Definition: qgis.h:92
QgsMessageBarItem::setLevel
QgsMessageBarItem * setLevel(Qgis::MessageLevel level)
Sets the message level for the item, which controls how the message bar is styled when the item is di...
Definition: qgsmessagebaritem.cpp:221
QgsMessageBar::createMessage
static QgsMessageBarItem * createMessage(const QString &text, QWidget *parent=nullptr)
Creates message bar item widget containing a message text to be displayed on the bar.
Definition: qgsmessagebar.cpp:363
QgsGuiUtils::scaleIconSize
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
Definition: qgsguiutils.cpp:245
QgsMessageBar::pushMessage
void pushMessage(const QString &text, Qgis::MessageLevel level=Qgis::Info, int duration=5)
A convenience method for pushing a message with the specified text to the bar.
Definition: qgsmessagebar.cpp:379
QgsMessageBar::widgetRemoved
void widgetRemoved(QgsMessageBarItem *item)
Emitted whenever an item was removed from the bar.
QgsMessageBar::pushInfo
void pushInfo(const QString &title, const QString &message)
Pushes a information message with default timeout to the message bar.
Definition: qgsmessagebar.cpp:199
qgsmessagelog.h