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