QGIS API Documentation  2.99.0-Master (585a4d3)
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 
23 #include <QWidget>
24 #include <QPalette>
25 #include <QStackedWidget>
26 #include <QProgressBar>
27 #include <QToolButton>
28 #include <QTimer>
29 #include <QGridLayout>
30 #include <QMenu>
31 #include <QMouseEvent>
32 #include <QLabel>
33 
34 QgsMessageBar::QgsMessageBar( QWidget *parent )
35  : QFrame( parent )
36 
37 {
38  QPalette pal = palette();
39  pal.setBrush( backgroundRole(), pal.window() );
40  setPalette( pal );
41  setAutoFillBackground( true );
42  setFrameShape( QFrame::StyledPanel );
43  setFrameShadow( QFrame::Plain );
44 
45  mLayout = new QGridLayout( this );
46  mLayout->setContentsMargins( 9, 1, 9, 1 );
47  setLayout( mLayout );
48 
49  mCountProgress = new QProgressBar( this );
50  mCountStyleSheet = QString( "QProgressBar { border: 1px solid rgba(0, 0, 0, 75%);"
51  " border-radius: 2px; background: rgba(0, 0, 0, 0);"
52  " image: url(:/images/themes/default/%1) }"
53  "QProgressBar::chunk { background-color: rgba(0, 0, 0, 30%); width: 5px; }" );
54 
55  mCountProgress->setStyleSheet( mCountStyleSheet.arg( QStringLiteral( "mIconTimerPause.png" ) ) );
56  mCountProgress->setObjectName( QStringLiteral( "mCountdown" ) );
57  mCountProgress->setFixedSize( 25, 14 );
58  mCountProgress->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
59  mCountProgress->setTextVisible( false );
60  mCountProgress->setRange( 0, 5 );
61  mCountProgress->setHidden( true );
62  mLayout->addWidget( mCountProgress, 0, 0, 1, 1 );
63 
64  mItemCount = new QLabel( this );
65  mItemCount->setObjectName( QStringLiteral( "mItemCount" ) );
66  mItemCount->setToolTip( tr( "Remaining messages" ) );
67  mItemCount->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Preferred );
68  mLayout->addWidget( mItemCount, 0, 2, 1, 1 );
69 
70  mCloseMenu = new QMenu( this );
71  mCloseMenu->setObjectName( QStringLiteral( "mCloseMenu" ) );
72  mActionCloseAll = new QAction( tr( "Close all" ), this );
73  mCloseMenu->addAction( mActionCloseAll );
74  connect( mActionCloseAll, &QAction::triggered, this, &QgsMessageBar::clearWidgets );
75 
76  mCloseBtn = new QToolButton( this );
77  mCloseMenu->setObjectName( QStringLiteral( "mCloseMenu" ) );
78  mCloseBtn->setToolTip( tr( "Close" ) );
79  mCloseBtn->setMinimumWidth( 40 );
80  mCloseBtn->setStyleSheet(
81  "QToolButton { background-color: rgba(0, 0, 0, 0); }"
82  "QToolButton::menu-button { background-color: rgba(0, 0, 0, 0); }" );
83  mCloseBtn->setCursor( Qt::PointingHandCursor );
84  mCloseBtn->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconClose.svg" ) ) );
85  mCloseBtn->setIconSize( QSize( 18, 18 ) );
86  mCloseBtn->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum );
87  mCloseBtn->setMenu( mCloseMenu );
88  mCloseBtn->setPopupMode( QToolButton::MenuButtonPopup );
89  connect( mCloseBtn, &QAbstractButton::clicked, this, static_cast < bool ( QgsMessageBar::* )() > ( &QgsMessageBar::popWidget ) );
90  mLayout->addWidget( mCloseBtn, 0, 3, 1, 1 );
91 
92  mCountdownTimer = new QTimer( this );
93  mCountdownTimer->setInterval( 1000 );
94  connect( mCountdownTimer, &QTimer::timeout, this, &QgsMessageBar::updateCountdown );
95 
96  connect( this, &QgsMessageBar::widgetAdded, this, &QgsMessageBar::updateItemCount );
97  connect( this, &QgsMessageBar::widgetRemoved, this, &QgsMessageBar::updateItemCount );
98 
99  // start hidden
100  setVisible( false );
101 }
102 
103 void QgsMessageBar::mousePressEvent( QMouseEvent *e )
104 {
105  if ( mCountProgress == childAt( e->pos() ) && e->button() == Qt::LeftButton )
106  {
107  if ( mCountdownTimer->isActive() )
108  {
109  mCountdownTimer->stop();
110  mCountProgress->setStyleSheet( mCountStyleSheet.arg( QStringLiteral( "mIconTimerContinue.png" ) ) );
111  }
112  else
113  {
114  mCountdownTimer->start();
115  mCountProgress->setStyleSheet( mCountStyleSheet.arg( QStringLiteral( "mIconTimerPause.png" ) ) );
116  }
117  }
118 }
119 
120 void QgsMessageBar::popItem( QgsMessageBarItem *item )
121 {
122  Q_ASSERT( item );
123 
124  if ( item != mCurrentItem && !mItems.contains( item ) )
125  return;
126 
127  if ( item == mCurrentItem )
128  {
129  if ( mCurrentItem )
130  {
131  QWidget *widget = mCurrentItem;
132  mLayout->removeWidget( widget );
133  mCurrentItem->hide();
134  disconnect( mCurrentItem, &QgsMessageBarItem::styleChanged, this, &QWidget::setStyleSheet );
135  mCurrentItem->deleteLater();
136  mCurrentItem = nullptr;
137  }
138 
139  if ( !mItems.isEmpty() )
140  {
141  showItem( mItems.at( 0 ) );
142  }
143  else
144  {
145  hide();
146  }
147  }
148  else
149  {
150  mItems.removeOne( item );
151  }
152 
153  emit widgetRemoved( item );
154 }
155 
157 {
158  if ( !item || !mCurrentItem )
159  return false;
160 
161  if ( item == mCurrentItem )
162  {
163  popItem( mCurrentItem );
164  return true;
165  }
166 
167  Q_FOREACH ( QgsMessageBarItem *existingItem, mItems )
168  {
169  if ( existingItem == item )
170  {
171  mItems.removeOne( existingItem );
172  existingItem->deleteLater();
173  return true;
174  }
175  }
176 
177  return false;
178 }
179 
181 {
182  if ( !mCurrentItem )
183  return false;
184 
185  resetCountdown();
186 
187  QgsMessageBarItem *item = mCurrentItem;
188  popItem( item );
189 
190  return true;
191 }
192 
194 {
195  if ( !mCurrentItem && mItems.empty() )
196  return true;
197 
198  while ( !mItems.isEmpty() )
199  {
200  popWidget();
201  }
202  popWidget();
203 
204  return !mCurrentItem && mItems.empty();
205 }
206 
207 void QgsMessageBar::pushSuccess( const QString &title, const QString &message )
208 {
209  pushMessage( title, message, Qgis::Success );
210 }
211 
212 void QgsMessageBar::pushInfo( const QString &title, const QString &message )
213 {
214  pushMessage( title, message, Qgis::Info );
215 }
216 
217 void QgsMessageBar::pushWarning( const QString &title, const QString &message )
218 {
219  pushMessage( title, message, Qgis::Warning );
220 }
221 
222 void QgsMessageBar::pushCritical( const QString &title, const QString &message )
223 {
224  pushMessage( title, message, Qgis::Critical );
225 }
226 
227 void QgsMessageBar::showItem( QgsMessageBarItem *item )
228 {
229  Q_ASSERT( item );
230 
231  if ( mCurrentItem )
232  disconnect( mCurrentItem, &QgsMessageBarItem::styleChanged, this, &QWidget::setStyleSheet );
233 
234  if ( item == mCurrentItem )
235  return;
236 
237  if ( mItems.contains( item ) )
238  mItems.removeOne( item );
239 
240  if ( mCurrentItem )
241  {
242  mItems.prepend( mCurrentItem );
243  mLayout->removeWidget( mCurrentItem );
244  mCurrentItem->hide();
245  }
246 
247  mCurrentItem = item;
248  mLayout->addWidget( item, 0, 1, 1, 1 );
249  mCurrentItem->show();
250 
251  if ( item->duration() > 0 )
252  {
253  mCountProgress->setRange( 0, item->duration() );
254  mCountProgress->setValue( item->duration() );
255  mCountProgress->setVisible( true );
256  mCountdownTimer->start();
257  }
258 
259  connect( mCurrentItem, &QgsMessageBarItem::styleChanged, this, &QWidget::setStyleSheet );
260  setStyleSheet( item->getStyleSheet() );
261  show();
262 
263  emit widgetAdded( item );
264 }
265 
267 {
268  resetCountdown();
269  // avoid duplicated widget
270  popWidget( item );
271  showItem( item );
272 
273  // Log all messages that are sent to the message bar into the message log so the
274  // user can get them back easier.
275  QString formattedTitle = QStringLiteral( "%1 : %2" ).arg( item->title(), item->text() );
276  QgsMessageLog::logMessage( formattedTitle, tr( "Messages" ), item->level() );
277 }
278 
279 QgsMessageBarItem *QgsMessageBar::pushWidget( QWidget *widget, Qgis::MessageLevel level, int duration )
280 {
281  QgsMessageBarItem *item = nullptr;
282  item = dynamic_cast<QgsMessageBarItem *>( widget );
283  if ( item )
284  {
285  item->setLevel( level )->setDuration( duration );
286  }
287  else
288  {
289  item = new QgsMessageBarItem( widget, level, duration );
290  }
291  pushItem( item );
292  return item;
293 }
294 
295 void QgsMessageBar::pushMessage( const QString &title, const QString &text, Qgis::MessageLevel level, int duration )
296 {
297  QgsMessageBarItem *item = new QgsMessageBarItem( title, text, level, duration );
298  pushItem( item );
299 }
300 
301 QgsMessageBarItem *QgsMessageBar::createMessage( const QString &text, QWidget *parent )
302 {
303  QgsMessageBarItem *item = new QgsMessageBarItem( text, Qgis::Info, 0, parent );
304  return item;
305 }
306 
307 QgsMessageBarItem *QgsMessageBar::createMessage( const QString &title, const QString &text, QWidget *parent )
308 {
309  return new QgsMessageBarItem( title, text, Qgis::Info, 0, parent );
310 }
311 
312 QgsMessageBarItem *QgsMessageBar::createMessage( QWidget *widget, QWidget *parent )
313 {
314  return new QgsMessageBarItem( widget, Qgis::Info, 0, parent );
315 }
316 
317 void QgsMessageBar::updateCountdown()
318 {
319  if ( !mCountdownTimer->isActive() )
320  {
321  resetCountdown();
322  return;
323  }
324  if ( mCountProgress->value() < 2 )
325  {
326  popWidget();
327  }
328  else
329  {
330  mCountProgress->setValue( mCountProgress->value() - 1 );
331  }
332 }
333 
334 void QgsMessageBar::resetCountdown()
335 {
336  if ( mCountdownTimer->isActive() )
337  mCountdownTimer->stop();
338 
339  mCountProgress->setStyleSheet( mCountStyleSheet.arg( QStringLiteral( "mIconTimerPause.png" ) ) );
340  mCountProgress->setVisible( false );
341 }
342 
343 void QgsMessageBar::updateItemCount()
344 {
345  mItemCount->setText( !mItems.isEmpty() ? tr( "%n more", "unread messages", mItems.count() ) : QString() );
346 
347  // do not show the down arrow for opening menu with "close all" if there is just one message
348  mCloseBtn->setMenu( !mItems.isEmpty() ? mCloseMenu : nullptr );
349  mCloseBtn->setPopupMode( !mItems.isEmpty() ? QToolButton::MenuButtonPopup : QToolButton::DelayedPopup );
350 }
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.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning)
add a message to the instance (and create it if necessary)
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.
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)
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.