QGIS API Documentation  3.23.0-Master (7c4a6de034)
qgsmessagelogviewer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmessagelogviewer.cpp - description
3  -------------------
4  begin : October 2011
5  copyright : (C) 2011 by Juergen E. Fischer
6  email : jef at norbit dot de
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 "qgsmessagelogviewer.h"
19 #include "qgsmessagelog.h"
20 #include "qgssettings.h"
21 #include "qgsapplication.h"
22 #include "qgsdockwidget.h"
23 
24 #include <QFile>
25 #include <QDateTime>
26 #include <QTableWidget>
27 #include <QToolButton>
28 #include <QStatusBar>
29 #include <QToolTip>
30 #include <QPlainTextEdit>
31 #include <QScrollBar>
32 #include <QDebug>
33 #include <QDesktopServices>
34 
35 QgsMessageLogViewer::QgsMessageLogViewer( QWidget *parent, Qt::WindowFlags fl )
36  : QDialog( parent, fl )
37 {
38  setupUi( this );
39 
40  connect( QgsApplication::messageLog(), static_cast<void ( QgsMessageLog::* )( const QString &, const QString &, Qgis::MessageLevel )>( &QgsMessageLog::messageReceived ),
41  this, static_cast<void ( QgsMessageLogViewer::* )( const QString &, const QString &, Qgis::MessageLevel )>( &QgsMessageLogViewer::logMessage ) );
42 
43  connect( tabWidget, &QTabWidget::tabCloseRequested, this, &QgsMessageLogViewer::closeTab );
44 
45  mTabBarContextMenu = new QMenu( this );
46  tabWidget->tabBar()->setContextMenuPolicy( Qt::CustomContextMenu );
47  connect( tabWidget->tabBar(), &QWidget::customContextMenuRequested, this, &QgsMessageLogViewer::showContextMenuForTabBar );
48 }
49 
50 void QgsMessageLogViewer::showContextMenuForTabBar( QPoint point )
51 {
52  if ( point.isNull() )
53  {
54  return;
55  }
56 
57  mTabBarContextMenu->clear();
58 
59  const int tabIndex = tabWidget->tabBar()->tabAt( point );
60 
61  QAction *actionCloseTab = new QAction( tr( "Close Tab" ), mTabBarContextMenu );
62  connect( actionCloseTab, &QAction::triggered, this, [this, tabIndex]
63  {
64  closeTab( tabIndex );
65  }
66  );
67  mTabBarContextMenu->addAction( actionCloseTab );
68 
69  QAction *actionCloseOtherTabs = new QAction( tr( "Close Other Tabs" ), mTabBarContextMenu );
70  actionCloseOtherTabs->setEnabled( tabWidget->tabBar()->count() > 1 );
71  connect( actionCloseOtherTabs, &QAction::triggered, this, [this, tabIndex]
72  {
73  int i;
74  for ( i = tabWidget->tabBar()->count() - 1; i >= 0; i-- )
75  {
76  if ( i != tabIndex )
77  {
78  closeTab( i );
79  }
80  }
81  }
82  );
83  mTabBarContextMenu->addAction( actionCloseOtherTabs );
84 
85  QAction *actionCloseAllTabs = new QAction( tr( "Close All Tabs" ), mTabBarContextMenu );
86  actionCloseAllTabs->setEnabled( tabWidget->tabBar()->count() > 0 );
87  connect( actionCloseAllTabs, &QAction::triggered, this, [this]
88  {
89  int i;
90  for ( i = tabWidget->tabBar()->count() - 1; i >= 0; i-- )
91  {
92  closeTab( i );
93  }
94  }
95  );
96  mTabBarContextMenu->addAction( actionCloseAllTabs );
97 
98  mTabBarContextMenu->exec( tabWidget->tabBar()->mapToGlobal( point ) );
99 }
100 
101 void QgsMessageLogViewer::closeEvent( QCloseEvent *e )
102 {
103  e->ignore();
104 }
105 
107 {
108 }
109 
110 void QgsMessageLogViewer::logMessage( const QString &message, const QString &tag, Qgis::MessageLevel level )
111 {
112  constexpr int MESSAGE_COUNT_LIMIT = 10000;
113  // Avoid logging too many messages, which might blow memory.
114  if ( mMessageLoggedCount == MESSAGE_COUNT_LIMIT )
115  return;
116  ++mMessageLoggedCount;
117 
118  QString cleanedTag = tag;
119  if ( cleanedTag.isNull() )
120  cleanedTag = tr( "General" );
121 
122  int i;
123  for ( i = 0; i < tabWidget->count() && tabWidget->tabText( i ).remove( QChar( '&' ) ) != cleanedTag; i++ );
124 
125  QPlainTextEdit *w = nullptr;
126  if ( i < tabWidget->count() )
127  {
128  w = qobject_cast<QPlainTextEdit *>( tabWidget->widget( i ) );
129  tabWidget->setCurrentIndex( i );
130  }
131  else
132  {
133  w = new QPlainTextEdit( this );
134  w->setReadOnly( true );
135  w->viewport()->installEventFilter( this );
136  tabWidget->addTab( w, cleanedTag );
137  tabWidget->setCurrentIndex( tabWidget->count() - 1 );
138  }
139 
140  QString levelString;
141  const QgsSettings settings;
142  const QPalette pal = qApp->palette();
143  const QString defaultColorName = pal.color( QPalette::WindowText ).name();
144  QString colorName;
145  switch ( level )
146  {
147  case Qgis::MessageLevel::Info:
148  levelString = QStringLiteral( "INFO" );
149  colorName = settings.value( QStringLiteral( "colors/info" ), QString() ).toString();
150  break;
151  case Qgis::MessageLevel::Warning:
152  levelString = QStringLiteral( "WARNING" );
153  colorName = settings.value( QStringLiteral( "colors/warning" ), QString() ).toString();
154  break;
155  case Qgis::MessageLevel::Critical:
156  levelString = QStringLiteral( "CRITICAL" );
157  colorName = settings.value( QStringLiteral( "colors/critical" ), QString() ).toString();
158  break;
159  case Qgis::MessageLevel::Success:
160  levelString = QStringLiteral( "SUCCESS" );
161  colorName = settings.value( QStringLiteral( "colors/success" ), QString() ).toString();
162  break;
163  case Qgis::MessageLevel::NoLevel:
164  levelString = QStringLiteral( "NONE" );
165  colorName = settings.value( QStringLiteral( "colors/default" ), QString() ).toString();
166  break;
167  }
168  const QColor color = QColor( !colorName.isEmpty() ? colorName : defaultColorName );
169 
170  const QString prefix = QStringLiteral( "<font color=\"%1\">%2 &nbsp;&nbsp;&nbsp; %3 &nbsp;&nbsp;&nbsp;</font>" )
171  .arg( color.name(), QDateTime::currentDateTime().toString( Qt::ISODate ), levelString );
172  QString cleanedMessage = message;
173  if ( mMessageLoggedCount == MESSAGE_COUNT_LIMIT )
174  cleanedMessage = tr( "Message log truncated" );
175 
176  cleanedMessage = cleanedMessage.prepend( prefix ).replace( '\n', QLatin1String( "<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;" ) );
177  w->appendHtml( cleanedMessage );
178  w->verticalScrollBar()->setValue( w->verticalScrollBar()->maximum() );
179  tabWidget->show();
180  emptyLabel->hide();
181 }
182 
183 void QgsMessageLogViewer::closeTab( int index )
184 {
185  tabWidget->removeTab( index );
186  if ( tabWidget->count() == 0 )
187  {
188  tabWidget->hide();
189  emptyLabel->show();
190  }
191 }
192 
193 bool QgsMessageLogViewer::eventFilter( QObject *object, QEvent *event )
194 {
195  switch ( event->type() )
196  {
197  case QEvent::MouseButtonPress:
198  {
199  if ( QPlainTextEdit *te = qobject_cast<QPlainTextEdit *>( object->parent() ) )
200  {
201  QMouseEvent *me = static_cast< QMouseEvent *>( event );
202  mClickedAnchor = ( me->button() & Qt::LeftButton ) ? te->anchorAt( me->pos() ) :
203  QString();
204  if ( !mClickedAnchor.isEmpty() )
205  return true;
206  }
207  break;
208  }
209 
210  case QEvent::MouseButtonRelease:
211  {
212  if ( QPlainTextEdit *te = qobject_cast<QPlainTextEdit *>( object->parent() ) )
213  {
214  QMouseEvent *me = static_cast< QMouseEvent *>( event );
215  const QString clickedAnchor = ( me->button() & Qt::LeftButton ) ? te->anchorAt( me->pos() ) :
216  QString();
217  if ( !clickedAnchor.isEmpty() && clickedAnchor == mClickedAnchor )
218  {
219  QDesktopServices::openUrl( mClickedAnchor );
220  return true;
221  }
222  }
223  break;
224  }
225 
226  default:
227  break;
228  }
229 
230  return QDialog::eventFilter( object, event );
231 }
MessageLevel
Level for messages This will be used both for message log and message bar in application.
Definition: qgis.h:107
static QgsMessageLog * messageLog()
Returns the application's message log.
A generic dialog widget for displaying QGIS log messages.
void closeEvent(QCloseEvent *e) override
bool eventFilter(QObject *obj, QEvent *ev) override
QgsMessageLogViewer(QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
Create a new message log viewer.
void logMessage(const QString &message, const QString &tag, Qgis::MessageLevel level)
Logs a message to the viewer.
Interface for logging messages from QGIS in GUI independent way.
Definition: qgsmessagelog.h:40
void messageReceived(const QString &message, const QString &tag, Qgis::MessageLevel level)
Emitted whenever the log receives a message.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.