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