| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430 |
- //##########################################################################
- //# #
- //# CLOUDCOMPARE #
- //# #
- //# This program is free software; you can redistribute it and/or modify #
- //# it under the terms of the GNU General Public License as published by #
- //# the Free Software Foundation; version 2 or later of the License. #
- //# #
- //# This program is distributed in the hope that it will be useful, #
- //# but WITHOUT ANY WARRANTY; without even the implied warranty of #
- //# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
- //# GNU General Public License for more details. #
- //# #
- //# COPYRIGHT: EDF R&D / TELECOM ParisTech (ENST-TSI) #
- //# #
- //##########################################################################
- #include "ccConsole.h"
- //Local
- #include "ccPersistentSettings.h"
- #include "mainwindow.h"
- //qCC_db
- #include <ccSingleton.h>
- //Qt
- #include <QApplication>
- #include <QClipboard>
- #include <QColor>
- #include <QKeyEvent>
- #include <QMessageBox>
- #include <QSettings>
- #include <QTextStream>
- #include <QThread>
- #include <QTime>
- //system
- #include <cassert>
- #ifdef QT_DEBUG
- #include <iostream>
- #endif
- /***************
- *** Globals ***
- ***************/
- //unique console instance
- static ccSingleton<ccConsole> s_console;
- bool ccConsole::s_showQtMessagesInConsole = false;
- bool ccConsole::s_redirectToStdOut = false;
- static int s_refreshCycle_ms = 1000;
- /*** ccCustomQListWidget ***/
- ccCustomQListWidget::ccCustomQListWidget(QWidget *parent)
- : QListWidget(parent)
- {
- }
- void ccCustomQListWidget::keyPressEvent(QKeyEvent *event)
- {
- if (event->matches(QKeySequence::Copy))
- {
- int itemsCount = count();
- QStringList strings;
- for (int i = 0; i < itemsCount; ++i)
- {
- if (item(i)->isSelected())
- {
- strings << item(i)->text();
- }
- }
-
- QApplication::clipboard()->setText(strings.join("\n"));
- }
- else
- {
- QListWidget::keyPressEvent(event);
- }
- }
- /*** ccConsole ***/
- void ccConsole::SetRefreshCycle(int cycle_ms/*=1000*/)
- {
- if (cycle_ms <= 0)
- {
- //invalid
- Warning("Invalid refresh cycle (can't be zero of negative)");
- return;
- }
- if (cycle_ms != s_refreshCycle_ms)
- {
- s_refreshCycle_ms = cycle_ms;
- if (s_console.instance && s_console.instance->autoRefresh())
- {
- // force the internal timer update
- s_console.instance->setAutoRefresh(false);
- s_console.instance->setAutoRefresh(true);
- }
- }
- }
- ccConsole* ccConsole::TheInstance(bool autoInit/*=true*/)
- {
- if (!s_console.instance && autoInit)
- {
- s_console.instance = new ccConsole;
- ccLog::RegisterInstance(s_console.instance);
- }
- return s_console.instance;
- }
- void ccConsole::ReleaseInstance(bool flush/*=true*/)
- {
- if (flush && s_console.instance)
- {
- //DGM: just in case some messages are still in the queue
- s_console.instance->refresh();
- }
- ccLog::RegisterInstance(nullptr);
- s_console.release();
- }
- ccConsole::ccConsole()
- : m_textDisplay(nullptr)
- , m_parentWidget(nullptr)
- , m_parentWindow(nullptr)
- , m_logStream(nullptr)
- {
- }
- ccConsole::~ccConsole()
- {
- setLogFile(QString()); //to close/delete any active stream
- }
- static void MyMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
- {
- #ifndef QT_DEBUG
- if (!ccConsole::QtMessagesEnabled())
- {
- return;
- }
- if (type == QtDebugMsg)
- {
- return;
- }
- #endif
- QString message = QString("[%1] ").arg(context.function) + msg; // QString("%1 (%1:%1, %1)").arg(msg).arg(context.file).arg(context.line).arg(context.function);
- //in this function, you can write the message to any stream!
- switch (type)
- {
- case QtDebugMsg:
- ccLog::PrintDebug(msg);
- break;
- case QtWarningMsg:
- message.prepend("[Qt WARNING] ");
- ccLog::Warning(message);
- break;
- case QtCriticalMsg:
- message.prepend("[Qt CRITICAL] ");
- ccLog::Warning(message);
- break;
- case QtFatalMsg:
- message.prepend("[Qt FATAL] ");
- ccLog::Warning(message);
- break;
- case QtInfoMsg:
- message.prepend("[Qt INFO] ");
- ccLog::Warning(message);
- break;
- }
-
- #ifdef QT_DEBUG
- // Also send the message to the console so we can look at the output when CC has quit
- // (in Qt Creator's Application Output for example)
- switch (type)
- {
- case QtDebugMsg:
- case QtWarningMsg:
- case QtInfoMsg:
- std::cout << message.toStdString() << std::endl;
- break;
-
- case QtCriticalMsg:
- case QtFatalMsg:
- std::cerr << message.toStdString() << std::endl;
- break;
- }
-
- #endif
- }
- void ccConsole::EnableQtMessages(bool state)
- {
- s_showQtMessagesInConsole = state;
- //save to persistent settings
- QSettings settings;
- settings.beginGroup(ccPS::Console());
- settings.setValue("QtMessagesEnabled", s_showQtMessagesInConsole);
- settings.endGroup();
- }
- void ccConsole::Init( QListWidget* textDisplay/*=nullptr*/,
- QWidget* parentWidget/*=nullptr*/,
- MainWindow* parentWindow/*=nullptr*/,
- bool redirectToStdOut/*=false*/)
- {
- //should be called only once!
- if (s_console.instance)
- {
- assert(false);
- return;
- }
-
- s_console.instance = new ccConsole;
- s_console.instance->m_textDisplay = textDisplay;
- s_console.instance->m_parentWidget = parentWidget;
- s_console.instance->m_parentWindow = parentWindow;
- s_redirectToStdOut = redirectToStdOut;
- if (s_redirectToStdOut)
- {
- // make the system console/terminal more responsive by removing any buffering
- setbuf(stdout, NULL);
- }
- //auto-start
- if (textDisplay)
- {
- //load from persistent settings
- QSettings settings;
- settings.beginGroup(ccPS::Console());
- s_showQtMessagesInConsole = settings.value("QtMessagesEnabled", false).toBool();
- settings.endGroup();
- //install : set the callback for Qt messages
- qInstallMessageHandler(MyMessageOutput);
- s_console.instance->setAutoRefresh(true);
- }
- ccLog::RegisterInstance(s_console.instance);
- }
- bool ccConsole::autoRefresh() const
- {
- return m_timer.isActive();
- }
- void ccConsole::setAutoRefresh(bool state)
- {
- if (state)
- {
- connect(&m_timer, &QTimer::timeout, this, &ccConsole::refresh);
- m_timer.start(s_refreshCycle_ms);
- }
- else
- {
- m_timer.stop();
- disconnect(&m_timer, &QTimer::timeout, this, &ccConsole::refresh);
- }
- }
- void ccConsole::refresh()
- {
- m_mutex.lock();
-
- if (!m_queue.isEmpty())
- {
- if (m_textDisplay || m_logStream)
- {
- for (auto messagePair : m_queue)
- {
- //destination: log file
- if (m_logStream)
- {
- *m_logStream << messagePair.first << endl;
- }
- //destination: console widget
- if (m_textDisplay)
- {
- //messagePair.first = message text
- QListWidgetItem* item = new QListWidgetItem(messagePair.first);
- //set color based on the message severity
- if ((messagePair.second & LOG_ERROR) == LOG_ERROR) // Error
- {
- item->setForeground(Qt::red);
- }
- else if ((messagePair.second & LOG_WARNING) == LOG_WARNING) // Warning
- {
- item->setForeground(Qt::darkRed);
- //we also force the console visibility if a warning message arrives!
- if (m_parentWindow)
- {
- m_parentWindow->forceConsoleDisplay();
- }
- }
- #ifdef QT_DEBUG
- else if (messagePair.second & DEBUG_FLAG) // Debug
- {
- item->setForeground(Qt::blue);
- }
- #endif
- m_textDisplay->addItem(item);
- }
- }
- if (m_logStream)
- {
- m_logFile.flush();
- }
- if (m_textDisplay)
- {
- m_textDisplay->scrollToBottom();
- }
- }
- m_queue.clear();
- }
- m_mutex.unlock();
- }
- void ccConsole::logMessage(const QString& message, int level)
- {
- //skip messages below the current 'verbosity' level
- if ((level & 7) < ccLog::VerbosityLevel())
- {
- return;
- }
- QString formatedMessage = QStringLiteral("[") + QTime::currentTime().toString() + QStringLiteral("] ") + message;
- if (s_redirectToStdOut)
- {
- printf("%s\n", qPrintable(formatedMessage));
- }
- if (m_textDisplay || m_logStream)
- {
- m_mutex.lock();
- m_queue.push_back(ConsoleItemType(formatedMessage, level));
- m_mutex.unlock();
- }
- #ifdef QT_DEBUG
- else if (!s_redirectToStdOut)
- {
- //Error
- if (level & LOG_ERROR)
- {
- if (level & DEBUG_FLAG)
- printf("ERR-DBG: ");
- else
- printf("ERR: ");
- }
- //Warning
- else if (level & LOG_WARNING)
- {
- if (level & DEBUG_FLAG)
- printf("WARN-DBG: ");
- else
- printf("WARN: ");
- }
- //Standard
- else
- {
- if (level & DEBUG_FLAG)
- printf("MSG-DBG: ");
- else
- printf("MSG: ");
- }
- printf(" %s\n", qPrintable(formatedMessage));
- }
- #endif
- //we display the error messages in a popup dialog
- if ( (level & LOG_ERROR)
- && qApp
- && m_parentWidget
- && QThread::currentThread() == qApp->thread()
- )
- {
- QMessageBox::warning(m_parentWidget, "Error", message);
- }
- }
- bool ccConsole::setLogFile(const QString& filename)
- {
- //close previous stream (if any)
- if (m_logStream)
- {
- m_mutex.lock();
- delete m_logStream;
- m_logStream = nullptr;
- m_mutex.unlock();
- if (m_logFile.isOpen())
- {
- m_logFile.close();
- }
- }
-
- if (!filename.isEmpty())
- {
- m_logFile.setFileName(filename);
- if (!m_logFile.open(QFile::Text| QFile::WriteOnly))
- {
- return Error(QString("[Console] Failed to open/create log file '%1'").arg(filename));
- }
- m_mutex.lock();
- m_logStream = new QTextStream(&m_logFile);
- m_mutex.unlock();
- setAutoRefresh(true);
- }
- return true;
- }
|