QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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(
61 "QProgressBar { border: 1px solid rgba(0, 0, 0, 75%);"
62 " border-radius: 2px; background: rgba(0, 0, 0, 0);"
63 " image: url(:/images/themes/default/%1) }"
64 "QProgressBar::chunk { background-color: rgba(0, 0, 0, 30%); width: 5px; }"
65 );
66
67 mCountProgress->setStyleSheet( mCountStyleSheet.arg( "mIconTimerPause.svg"_L1 ) );
68 mCountProgress->setObjectName( u"mCountdown"_s );
69 const int barWidth = std::max( 25.0, Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 1.25 );
70 const int barHeight = std::max( 14.0, Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 0.7 );
71 mCountProgress->setFixedSize( barWidth, barHeight );
72 mCountProgress->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
73 mCountProgress->setTextVisible( false );
74 mCountProgress->setRange( 0, 5 );
75 mCountProgress->setHidden( true );
76 mLayout->addWidget( mCountProgress, 0, 0, 1, 1 );
77
78 mItemCount = new QLabel( this );
79 mItemCount->setObjectName( u"mItemCount"_s );
80 mItemCount->setToolTip( tr( "Remaining messages" ) );
81 mItemCount->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Preferred );
82 mLayout->addWidget( mItemCount, 0, 2, 1, 1 );
83
84 mCloseMenu = new QMenu( this );
85 mCloseMenu->setObjectName( u"mCloseMenu"_s );
86 mActionCloseAll = new QAction( tr( "Close All" ), this );
87 mCloseMenu->addAction( mActionCloseAll );
88 connect( mActionCloseAll, &QAction::triggered, this, &QgsMessageBar::clearWidgets );
89
90 mCloseBtn = new QToolButton( this );
91 mCloseMenu->setObjectName( u"mCloseMenu"_s );
92 mCloseBtn->setToolTip( tr( "Close" ) );
93 mCloseBtn->setMinimumWidth( QgsGuiUtils::scaleIconSize( 44 ) );
94 mCloseBtn->setStyleSheet(
95 "QToolButton { border:none; background-color: rgba(0, 0, 0, 0); }"
96 "QToolButton::menu-button { border:none; background-color: rgba(0, 0, 0, 0); }"
97 );
98 mCloseBtn->setCursor( Qt::PointingHandCursor );
99 mCloseBtn->setIcon( QgsApplication::getThemeIcon( u"/mIconClose.svg"_s ) );
100
101 const int iconSize = std::max( 18.0, Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 0.9 );
102 mCloseBtn->setIconSize( QSize( iconSize, iconSize ) );
103 mCloseBtn->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum );
104 mCloseBtn->setMenu( mCloseMenu );
105 mCloseBtn->setPopupMode( QToolButton::MenuButtonPopup );
106 connect( mCloseBtn, &QAbstractButton::clicked, this, static_cast<bool ( QgsMessageBar::* )()>( &QgsMessageBar::popWidget ) );
107 mLayout->addWidget( mCloseBtn, 0, 3, 1, 1 );
108
109 mCountdownTimer = new QTimer( this );
110 mCountdownTimer->setInterval( 1000 );
111 connect( mCountdownTimer, &QTimer::timeout, this, &QgsMessageBar::updateCountdown );
112
113 connect( this, &QgsMessageBar::widgetAdded, this, &QgsMessageBar::updateItemCount );
114 connect( this, &QgsMessageBar::widgetRemoved, this, &QgsMessageBar::updateItemCount );
115
116 // start hidden
117 setVisible( false );
118}
119
120void QgsMessageBar::mousePressEvent( QMouseEvent *e )
121{
122 if ( mCountProgress == childAt( e->pos() ) && e->button() == Qt::LeftButton )
123 {
124 if ( mCountdownTimer->isActive() )
125 {
126 mCountdownTimer->stop();
127 mCountProgress->setStyleSheet( mCountStyleSheet.arg( "mIconTimerContinue.svg"_L1 ) );
128 }
129 else
130 {
131 mCountdownTimer->start();
132 mCountProgress->setStyleSheet( mCountStyleSheet.arg( "mIconTimerPause.svg"_L1 ) );
133 }
134 }
135}
136
137void QgsMessageBar::popItem( QgsMessageBarItem *item )
138{
139 Q_ASSERT( item );
140
141 if ( !mItems.contains( item ) )
142 return;
143
144 if ( item == mItems.at( 0 ) )
145 {
146 mItems.removeOne( item );
147 mLayout->removeWidget( item );
148 item->hide();
149 disconnect( item, &QgsMessageBarItem::styleChanged, this, &QWidget::setStyleSheet );
150 item->deleteLater();
151
152 if ( !mItems.isEmpty() )
153 {
154 showItem( mItems.at( 0 ) );
155 }
156 else
157 {
158 hide();
159 }
160 }
161 else
162 {
163 mItems.removeOne( item );
164 item->deleteLater();
165 }
166
167 emit widgetRemoved( item );
168}
169
171{
172 if ( !item || !mItems.contains( item ) )
173 return false;
174
175 popItem( item );
176 return true;
177}
178
180{
181 if ( mItems.empty() )
182 return false;
183
184 resetCountdown();
185
186 popItem( mItems.at( 0 ) );
187
188 return true;
189}
190
192{
193 if ( mItems.empty() )
194 return true;
195
196 while ( !mItems.isEmpty() )
197 {
198 popWidget();
199 }
200
201 return true;
202}
203
204void QgsMessageBar::pushSuccess( const QString &title, const QString &message )
205{
206 pushMessage( title, message, Qgis::MessageLevel::Success );
207}
208
209void QgsMessageBar::pushInfo( const QString &title, const QString &message )
210{
211 pushMessage( title, message, Qgis::MessageLevel::Info );
212}
213
214void QgsMessageBar::pushWarning( const QString &title, const QString &message )
215{
216 pushMessage( title, message, Qgis::MessageLevel::Warning );
217}
218
219void QgsMessageBar::pushCritical( const QString &title, const QString &message )
220{
221 pushMessage( title, message, Qgis::MessageLevel::Critical );
222}
223
225{
226 // critical/warning messages don't auto dismiss by default
227 switch ( level )
228 {
232 {
233 const QgsSettings settings;
234 return settings.value( u"qgis/messageTimeout"_s, 5 ).toInt();
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:6591
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)
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,...