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