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