QGIS API Documentation  3.2.0-Bonn (bc43194)
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  mLayout->setContentsMargins( 9, 1, 9, 1 );
48  setLayout( mLayout );
49 
50  mCountProgress = new QProgressBar( this );
51  mCountStyleSheet = QString( "QProgressBar { border: 1px solid rgba(0, 0, 0, 75%);"
52  " border-radius: 2px; background: rgba(0, 0, 0, 0);"
53  " image: url(:/images/themes/default/%1) }"
54  "QProgressBar::chunk { background-color: rgba(0, 0, 0, 30%); width: 5px; }" );
55 
56  mCountProgress->setStyleSheet( mCountStyleSheet.arg( QStringLiteral( "mIconTimerPause.svg" ) ) );
57  mCountProgress->setObjectName( QStringLiteral( "mCountdown" ) );
58  mCountProgress->setFixedSize( 25, 14 );
59  mCountProgress->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
60  mCountProgress->setTextVisible( false );
61  mCountProgress->setRange( 0, 5 );
62  mCountProgress->setHidden( true );
63  mLayout->addWidget( mCountProgress, 0, 0, 1, 1 );
64 
65  mItemCount = new QLabel( this );
66  mItemCount->setObjectName( QStringLiteral( "mItemCount" ) );
67  mItemCount->setToolTip( tr( "Remaining messages" ) );
68  mItemCount->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Preferred );
69  mLayout->addWidget( mItemCount, 0, 2, 1, 1 );
70 
71  mCloseMenu = new QMenu( this );
72  mCloseMenu->setObjectName( QStringLiteral( "mCloseMenu" ) );
73  mActionCloseAll = new QAction( tr( "Close All" ), this );
74  mCloseMenu->addAction( mActionCloseAll );
75  connect( mActionCloseAll, &QAction::triggered, this, &QgsMessageBar::clearWidgets );
76 
77  mCloseBtn = new QToolButton( this );
78  mCloseMenu->setObjectName( QStringLiteral( "mCloseMenu" ) );
79  mCloseBtn->setToolTip( tr( "Close" ) );
80  mCloseBtn->setMinimumWidth( 40 );
81  mCloseBtn->setStyleSheet(
82  "QToolButton { background-color: rgba(0, 0, 0, 0); }"
83  "QToolButton::menu-button { background-color: rgba(0, 0, 0, 0); }" );
84  mCloseBtn->setCursor( Qt::PointingHandCursor );
85  mCloseBtn->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconClose.svg" ) ) );
86  mCloseBtn->setIconSize( QSize( 18, 18 ) );
87  mCloseBtn->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum );
88  mCloseBtn->setMenu( mCloseMenu );
89  mCloseBtn->setPopupMode( QToolButton::MenuButtonPopup );
90  connect( mCloseBtn, &QAbstractButton::clicked, this, static_cast < bool ( QgsMessageBar::* )() > ( &QgsMessageBar::popWidget ) );
91  mLayout->addWidget( mCloseBtn, 0, 3, 1, 1 );
92 
93  mCountdownTimer = new QTimer( this );
94  mCountdownTimer->setInterval( 1000 );
95  connect( mCountdownTimer, &QTimer::timeout, this, &QgsMessageBar::updateCountdown );
96 
97  connect( this, &QgsMessageBar::widgetAdded, this, &QgsMessageBar::updateItemCount );
98  connect( this, &QgsMessageBar::widgetRemoved, this, &QgsMessageBar::updateItemCount );
99 
100  // start hidden
101  setVisible( false );
102 }
103 
104 void QgsMessageBar::mousePressEvent( QMouseEvent *e )
105 {
106  if ( mCountProgress == childAt( e->pos() ) && e->button() == Qt::LeftButton )
107  {
108  if ( mCountdownTimer->isActive() )
109  {
110  mCountdownTimer->stop();
111  mCountProgress->setStyleSheet( mCountStyleSheet.arg( QStringLiteral( "mIconTimerContinue.svg" ) ) );
112  }
113  else
114  {
115  mCountdownTimer->start();
116  mCountProgress->setStyleSheet( mCountStyleSheet.arg( QStringLiteral( "mIconTimerPause.svg" ) ) );
117  }
118  }
119 }
120 
121 void QgsMessageBar::popItem( QgsMessageBarItem *item )
122 {
123  Q_ASSERT( item );
124 
125  if ( item != mCurrentItem && !mItems.contains( item ) )
126  return;
127 
128  if ( item == mCurrentItem )
129  {
130  if ( mCurrentItem )
131  {
132  QWidget *widget = mCurrentItem;
133  mLayout->removeWidget( widget );
134  mCurrentItem->hide();
135  disconnect( mCurrentItem, &QgsMessageBarItem::styleChanged, this, &QWidget::setStyleSheet );
136  mCurrentItem->deleteLater();
137  mCurrentItem = nullptr;
138  }
139 
140  if ( !mItems.isEmpty() )
141  {
142  showItem( mItems.at( 0 ) );
143  }
144  else
145  {
146  hide();
147  }
148  }
149  else
150  {
151  mItems.removeOne( item );
152  }
153 
154  emit widgetRemoved( item );
155 }
156 
158 {
159  if ( !item || !mCurrentItem )
160  return false;
161 
162  if ( item == mCurrentItem )
163  {
164  popItem( mCurrentItem );
165  return true;
166  }
167 
168  Q_FOREACH ( QgsMessageBarItem *existingItem, mItems )
169  {
170  if ( existingItem == item )
171  {
172  mItems.removeOne( existingItem );
173  existingItem->deleteLater();
174  return true;
175  }
176  }
177 
178  return false;
179 }
180 
182 {
183  if ( !mCurrentItem )
184  return false;
185 
186  resetCountdown();
187 
188  QgsMessageBarItem *item = mCurrentItem;
189  popItem( item );
190 
191  return true;
192 }
193 
195 {
196  if ( !mCurrentItem && mItems.empty() )
197  return true;
198 
199  while ( !mItems.isEmpty() )
200  {
201  popWidget();
202  }
203  popWidget();
204 
205  return !mCurrentItem && mItems.empty();
206 }
207 
208 void QgsMessageBar::pushSuccess( const QString &title, const QString &message )
209 {
210  pushMessage( title, message, Qgis::Success );
211 }
212 
213 void QgsMessageBar::pushInfo( const QString &title, const QString &message )
214 {
215  pushMessage( title, message, Qgis::Info );
216 }
217 
218 void QgsMessageBar::pushWarning( const QString &title, const QString &message )
219 {
220  pushMessage( title, message, Qgis::Warning );
221 }
222 
223 void QgsMessageBar::pushCritical( const QString &title, const QString &message )
224 {
225  pushMessage( title, message, Qgis::Critical );
226 }
227 
228 void QgsMessageBar::showItem( QgsMessageBarItem *item )
229 {
230  Q_ASSERT( item );
231 
232  if ( mCurrentItem )
233  disconnect( mCurrentItem, &QgsMessageBarItem::styleChanged, this, &QWidget::setStyleSheet );
234 
235  if ( item == mCurrentItem )
236  return;
237 
238  if ( mItems.contains( item ) )
239  mItems.removeOne( item );
240 
241  if ( mCurrentItem )
242  {
243  mItems.prepend( mCurrentItem );
244  mLayout->removeWidget( mCurrentItem );
245  mCurrentItem->hide();
246  }
247 
248  mCurrentItem = item;
249  mLayout->addWidget( item, 0, 1, 1, 1 );
250  mCurrentItem->show();
251 
252  if ( item->duration() > 0 )
253  {
254  mCountProgress->setRange( 0, item->duration() );
255  mCountProgress->setValue( item->duration() );
256  mCountProgress->setVisible( true );
257  mCountdownTimer->start();
258  }
259 
260  connect( mCurrentItem, &QgsMessageBarItem::styleChanged, this, &QWidget::setStyleSheet );
261  setStyleSheet( item->getStyleSheet() );
262  show();
263 
264  emit widgetAdded( item );
265 }
266 
268 {
269  resetCountdown();
270  // avoid duplicated widget
271  popWidget( item );
272  showItem( item );
273 
274  // Log all messages that are sent to the message bar into the message log so the
275  // user can get them back easier.
276  QString formattedTitle = QStringLiteral( "%1 : %2" ).arg( item->title(), item->text() );
277  QgsMessageLog::logMessage( formattedTitle, tr( "Messages" ), item->level() );
278 }
279 
280 QgsMessageBarItem *QgsMessageBar::pushWidget( QWidget *widget, Qgis::MessageLevel level, int duration )
281 {
282  QgsMessageBarItem *item = nullptr;
283  item = dynamic_cast<QgsMessageBarItem *>( widget );
284  if ( item )
285  {
286  item->setLevel( level )->setDuration( duration );
287  }
288  else
289  {
290  item = new QgsMessageBarItem( widget, level, duration );
291  }
292  pushItem( item );
293  return item;
294 }
295 
296 void QgsMessageBar::pushMessage( const QString &title, const QString &text, Qgis::MessageLevel level, int duration )
297 {
298  QgsMessageBarItem *item = new QgsMessageBarItem( title, text, level, duration );
299  pushItem( item );
300 }
301 
302 void QgsMessageBar::pushMessage( const QString &title, const QString &text, const QString &showMore, Qgis::MessageLevel level, int duration )
303 {
305  mv->setWindowTitle( title );
306  mv->setMessageAsPlainText( text + "\n\n" + showMore );
307 
308  QToolButton *showMoreButton = new QToolButton();
309  QAction *act = new QAction( showMoreButton );
310  act->setText( tr( "Show more" ) );
311  showMoreButton->setStyleSheet( QStringLiteral( "background-color: rgba(255, 255, 255, 0); color: black; text-decoration: underline;" ) );
312  showMoreButton->setCursor( Qt::PointingHandCursor );
313  showMoreButton->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Preferred );
314  showMoreButton->addAction( act );
315  showMoreButton->setDefaultAction( act );
316  connect( showMoreButton, &QToolButton::triggered, mv, &QDialog::exec );
317  connect( showMoreButton, &QToolButton::triggered, showMoreButton, &QObject::deleteLater );
318 
320  title,
321  text,
322  showMoreButton,
323  level,
324  duration );
325  pushItem( item );
326 }
327 
328 QgsMessageBarItem *QgsMessageBar::createMessage( const QString &text, QWidget *parent )
329 {
330  QgsMessageBarItem *item = new QgsMessageBarItem( text, Qgis::Info, 0, parent );
331  return item;
332 }
333 
334 QgsMessageBarItem *QgsMessageBar::createMessage( const QString &title, const QString &text, QWidget *parent )
335 {
336  return new QgsMessageBarItem( title, text, Qgis::Info, 0, parent );
337 }
338 
339 QgsMessageBarItem *QgsMessageBar::createMessage( QWidget *widget, QWidget *parent )
340 {
341  return new QgsMessageBarItem( widget, Qgis::Info, 0, parent );
342 }
343 
344 void QgsMessageBar::updateCountdown()
345 {
346  if ( !mCountdownTimer->isActive() )
347  {
348  resetCountdown();
349  return;
350  }
351  if ( mCountProgress->value() < 2 )
352  {
353  popWidget();
354  }
355  else
356  {
357  mCountProgress->setValue( mCountProgress->value() - 1 );
358  }
359 }
360 
361 void QgsMessageBar::resetCountdown()
362 {
363  if ( mCountdownTimer->isActive() )
364  mCountdownTimer->stop();
365 
366  mCountProgress->setStyleSheet( mCountStyleSheet.arg( QStringLiteral( "mIconTimerPause.svg" ) ) );
367  mCountProgress->setVisible( false );
368 }
369 
370 void QgsMessageBar::updateItemCount()
371 {
372  mItemCount->setText( !mItems.isEmpty() ? tr( "%n more", "unread messages", mItems.count() ) : QString() );
373 
374  // do not show the down arrow for opening menu with "close all" if there is just one message
375  mCloseBtn->setMenu( !mItems.isEmpty() ? mCloseMenu : nullptr );
376  mCloseBtn->setPopupMode( !mItems.isEmpty() ? QToolButton::MenuButtonPopup : QToolButton::DelayedPopup );
377 }
QgsMessageBar(QWidget *parent=nullptr)
Constructor for QgsMessageBar.
void mousePressEvent(QMouseEvent *e) override
void pushInfo(const QString &title, const QString &message)
Pushes a information message with default timeout to the message bar.
QString title() const
Returns the title for the message.
void widgetRemoved(QgsMessageBarItem *item)
emitted when a message widget was removed from the bar
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 s...
A bar for displaying non-blocking messages to the user.
Definition: qgsmessagebar.h:45
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
bool clearWidgets()
Remove all items from the bar&#39;s widget list.
int duration() const
returns the duration in second of the message
MessageLevel
Level for messages This will be used both for message log and message bar in application.
Definition: qgis.h:78
void pushSuccess(const QString &title, const QString &message)
Pushes a success message with default timeout to the message bar.
Qgis::MessageLevel level() const
Returns the message level for the message.
static QgsMessageBarItem * createMessage(const QString &text, QWidget *parent=nullptr)
make out a widget containing a message to be displayed on the bar
void pushWarning(const QString &title, const QString &message)
Pushes a warning with default timeout to the message bar.
QString text() const
Returns the text for the message.
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).
void setMessageAsPlainText(const QString &msg)
void widgetAdded(QgsMessageBarItem *item)
emitted when a message widget is added to the bar
void pushMessage(const QString &text, Qgis::MessageLevel level=Qgis::Info, int duration=5)
convenience method for pushing a message to the bar
Definition: qgsmessagebar.h:88
QString getStyleSheet()
returns the styleSheet
void pushItem(QgsMessageBarItem *item)
Display a message item on the bar after hiding the currently visible one and putting it in a stack...
QgsMessageBarItem * setDuration(int duration)
A generic message view for displaying QGIS messages.
void styleChanged(const QString &styleSheet)
emitted when the message level has changed
bool popWidget()
Remove the currently displayed widget from the bar and display the next in the stack if any or hide t...
QgsMessageBarItem * setLevel(Qgis::MessageLevel level)
void pushCritical(const QString &title, const QString &message)
Pushes a critical warning with default timeout to the message bar.