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