QGIS API Documentation 3.41.0-Master (3440c17df1d)
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 mCloseBtn->setCursor( Qt::PointingHandCursor );
91 mCloseBtn->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconClose.svg" ) ) );
92
93 const int iconSize = std::max( 18.0, Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 0.9 );
94 mCloseBtn->setIconSize( QSize( iconSize, iconSize ) );
95 mCloseBtn->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum );
96 mCloseBtn->setMenu( mCloseMenu );
97 mCloseBtn->setPopupMode( QToolButton::MenuButtonPopup );
98 connect( mCloseBtn, &QAbstractButton::clicked, this, static_cast < bool ( QgsMessageBar::* )() > ( &QgsMessageBar::popWidget ) );
99 mLayout->addWidget( mCloseBtn, 0, 3, 1, 1 );
100
101 mCountdownTimer = new QTimer( this );
102 mCountdownTimer->setInterval( 1000 );
103 connect( mCountdownTimer, &QTimer::timeout, this, &QgsMessageBar::updateCountdown );
104
105 connect( this, &QgsMessageBar::widgetAdded, this, &QgsMessageBar::updateItemCount );
106 connect( this, &QgsMessageBar::widgetRemoved, this, &QgsMessageBar::updateItemCount );
107
108 // start hidden
109 setVisible( false );
110}
111
112void QgsMessageBar::mousePressEvent( QMouseEvent *e )
113{
114 if ( mCountProgress == childAt( e->pos() ) && e->button() == Qt::LeftButton )
115 {
116 if ( mCountdownTimer->isActive() )
117 {
118 mCountdownTimer->stop();
119 mCountProgress->setStyleSheet( mCountStyleSheet.arg( QLatin1String( "mIconTimerContinue.svg" ) ) );
120 }
121 else
122 {
123 mCountdownTimer->start();
124 mCountProgress->setStyleSheet( mCountStyleSheet.arg( QLatin1String( "mIconTimerPause.svg" ) ) );
125 }
126 }
127}
128
129void QgsMessageBar::popItem( QgsMessageBarItem *item )
130{
131 Q_ASSERT( item );
132
133 if ( !mItems.contains( item ) )
134 return;
135
136 if ( item == mItems.at( 0 ) )
137 {
138 mItems.removeOne( item );
139 mLayout->removeWidget( item );
140 item->hide();
141 disconnect( item, &QgsMessageBarItem::styleChanged, this, &QWidget::setStyleSheet );
142 item->deleteLater();
143
144 if ( !mItems.isEmpty() )
145 {
146 showItem( mItems.at( 0 ) );
147 }
148 else
149 {
150 hide();
151 }
152 }
153 else
154 {
155 mItems.removeOne( item );
156 item->deleteLater();
157 }
158
159 emit widgetRemoved( item );
160}
161
163{
164 if ( !item || !mItems.contains( item ) )
165 return false;
166
167 popItem( item );
168 return true;
169}
170
172{
173 if ( mItems.empty() )
174 return false;
175
176 resetCountdown();
177
178 popItem( mItems.at( 0 ) );
179
180 return true;
181}
182
184{
185 if ( mItems.empty() )
186 return true;
187
188 while ( !mItems.isEmpty() )
189 {
190 popWidget();
191 }
192
193 return true;
194}
195
196void QgsMessageBar::pushSuccess( const QString &title, const QString &message )
197{
198 pushMessage( title, message, Qgis::MessageLevel::Success );
199}
200
201void QgsMessageBar::pushInfo( const QString &title, const QString &message )
202{
203 pushMessage( title, message, Qgis::MessageLevel::Info );
204}
205
206void QgsMessageBar::pushWarning( const QString &title, const QString &message )
207{
208 pushMessage( title, message, Qgis::MessageLevel::Warning );
209}
210
211void QgsMessageBar::pushCritical( const QString &title, const QString &message )
212{
213 pushMessage( title, message, Qgis::MessageLevel::Critical );
214}
215
217{
218 // critical/warning messages don't auto dismiss by default
219 switch ( level )
220 {
224 {
225 const QgsSettings settings;
226 return settings.value( QStringLiteral( "qgis/messageTimeout" ), 5 ).toInt();
227 }
228
231 return 0;
232 }
233 return 0;
234}
235
236void QgsMessageBar::showItem( QgsMessageBarItem *item )
237{
238 Q_ASSERT( item );
239
240 if ( !mItems.empty() )
241 disconnect( mItems.at( 0 ), &QgsMessageBarItem::styleChanged, this, &QWidget::setStyleSheet );
242
243 if ( mItems.count() >= MAX_ITEMS )
244 removeLowestPriorityOldestItem();
245
246 if ( !mItems.empty() )
247 {
248 mLayout->removeWidget( mItems.at( 0 ) );
249 mItems.at( 0 )->hide();
250 }
251
252 if ( mItems.contains( item ) )
253 mItems.removeOne( item );
254 mItems.prepend( item );
255
256 mLayout->addWidget( item, 0, 1, 1, 1 );
257 item->show();
258
259 if ( item->duration() > 0 )
260 {
261 mCountProgress->setRange( 0, item->duration() );
262 mCountProgress->setValue( item->duration() );
263 mCountProgress->setVisible( true );
264 mCountdownTimer->start();
265 }
266
267 connect( item, &QgsMessageBarItem::styleChanged, this, &QWidget::setStyleSheet );
268
269 if ( item->level() != mPrevLevel )
270 {
271 setStyleSheet( item->getStyleSheet() );
272 mPrevLevel = item->level();
273 }
274
275 show();
276
277 emit widgetAdded( item );
278}
279
280void QgsMessageBar::removeLowestPriorityOldestItem()
281{
283 {
284 for ( int i = mItems.size() - 1; i >= 0; --i )
285 {
286 QgsMessageBarItem *item = mItems.at( i );
287 if ( item->level() == level )
288 {
289 popItem( item );
290 return;
291 }
292 }
293 }
294}
295
297{
298 resetCountdown();
299
300 item->mMessageBar = this;
301
302 // avoid duplicated widget
303 popWidget( item );
304 showItem( item );
305
306 // Log all (non-empty) messages that are sent to the message bar into the message log so the
307 // user can get them back easier.
308 QString formattedTitle;
309 if ( !item->title().isEmpty() && !item->text().isEmpty() )
310 formattedTitle = QStringLiteral( "%1 : %2" ).arg( item->title(), item->text() );
311 else if ( !item->title().isEmpty() )
312 formattedTitle = item->title();
313 else if ( !item->text().isEmpty() )
314 formattedTitle = item->text();
315
316 if ( !formattedTitle.isEmpty() )
317 QgsMessageLog::logMessage( formattedTitle, tr( "Messages" ), item->level() );
318}
319
320QgsMessageBarItem *QgsMessageBar::pushWidget( QWidget *widget, Qgis::MessageLevel level, int duration )
321{
322 QgsMessageBarItem *item = nullptr;
323 item = dynamic_cast<QgsMessageBarItem *>( widget );
324 if ( item )
325 {
326 item->setLevel( level )->setDuration( duration );
327 }
328 else
329 {
330 item = new QgsMessageBarItem( widget, level, duration );
331 }
332 pushItem( item );
333 return item;
334}
335
336void QgsMessageBar::pushMessage( const QString &title, const QString &text, Qgis::MessageLevel level, int duration )
337{
338 // block duplicate items, avoids flooding (and freezing) of the main window
339 for ( auto it = mItems.constBegin(); it != mItems.constEnd(); ++it )
340 {
341 if ( level == ( *it )->level() && title == ( *it )->title() && text == ( *it )->text() )
342 return;
343 }
344
345 QgsMessageBarItem *item = new QgsMessageBarItem( title, text, level, duration );
346 pushItem( item );
347}
348
349void QgsMessageBar::pushMessage( const QString &title, const QString &text, const QString &showMore, Qgis::MessageLevel level, int duration )
350{
352 mv->setWindowTitle( title );
353 mv->setMessageAsPlainText( text + "\n\n" + showMore );
354
355 QToolButton *showMoreButton = new QToolButton();
356 QAction *act = new QAction( showMoreButton );
357 act->setText( tr( "Show more" ) );
358 showMoreButton->setStyleSheet( QStringLiteral( "background-color: rgba(255, 255, 255, 0); color: black; text-decoration: underline;" ) );
359 showMoreButton->setCursor( Qt::PointingHandCursor );
360 showMoreButton->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Preferred );
361 showMoreButton->addAction( act );
362 showMoreButton->setDefaultAction( act );
363 connect( showMoreButton, &QToolButton::triggered, mv, &QDialog::exec );
364 connect( showMoreButton, &QToolButton::triggered, showMoreButton, &QObject::deleteLater );
365
366 if ( duration < 0 )
367 {
368 duration = defaultMessageTimeout( level );
369 }
370
372 title,
373 text,
374 showMoreButton,
375 level,
376 duration );
377 pushItem( item );
378}
379
381{
382 return mItems.value( 0 );
383}
384
385QList<QgsMessageBarItem *> QgsMessageBar::items()
386{
387 return mItems;
388}
389
390QgsMessageBarItem *QgsMessageBar::createMessage( const QString &text, QWidget *parent )
391{
392 QgsMessageBarItem *item = new QgsMessageBarItem( text, Qgis::MessageLevel::Info, 0, parent );
393 return item;
394}
395
396QgsMessageBarItem *QgsMessageBar::createMessage( const QString &title, const QString &text, QWidget *parent )
397{
398 return new QgsMessageBarItem( title, text, Qgis::MessageLevel::Info, 0, parent );
399}
400
401QgsMessageBarItem *QgsMessageBar::createMessage( QWidget *widget, QWidget *parent )
402{
403 return new QgsMessageBarItem( widget, Qgis::MessageLevel::Info, 0, parent );
404}
405
406void QgsMessageBar::pushMessage( const QString &text, Qgis::MessageLevel level, int duration )
407{
408 pushMessage( QString(), text, level, duration );
409}
410
411void QgsMessageBar::updateCountdown()
412{
413 if ( !mCountdownTimer->isActive() )
414 {
415 resetCountdown();
416 return;
417 }
418 if ( mCountProgress->value() < 2 )
419 {
420 popWidget();
421 }
422 else
423 {
424 mCountProgress->setValue( mCountProgress->value() - 1 );
425 }
426}
427
428void QgsMessageBar::resetCountdown()
429{
430 if ( mCountdownTimer->isActive() )
431 mCountdownTimer->stop();
432
433 mCountProgress->setStyleSheet( mCountStyleSheet.arg( QLatin1String( "mIconTimerPause.svg" ) ) );
434 mCountProgress->setVisible( false );
435}
436
437void QgsMessageBar::updateItemCount()
438{
439 const bool moreMessages = mItems.count() > 1;
440 mItemCount->setText( moreMessages ? tr( "%n more", "unread messages", mItems.count() - 1 ) : QString() );
441
442 // do not show the down arrow for opening menu with "close all" if there is just one message
443 mCloseBtn->setMenu( moreMessages ? mCloseMenu : nullptr );
444 mCloseBtn->setPopupMode( moreMessages ? QToolButton::MenuButtonPopup : QToolButton::DelayedPopup );
445}
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:5667
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,...