Improve message handling from threads.
Only GUI thread can show message boxes. --HG-- branch : release
This commit is contained in:
parent
10a20b08b8
commit
47e3f9f7c7
|
@ -37,6 +37,7 @@
|
||||||
#include "../vmisc/logging.h"
|
#include "../vmisc/logging.h"
|
||||||
#include "../vmisc/vsysexits.h"
|
#include "../vmisc/vsysexits.h"
|
||||||
#include "../vmisc/diagnostic.h"
|
#include "../vmisc/diagnostic.h"
|
||||||
|
#include "../vmisc/qt_dispatch/qt_dispatch.h"
|
||||||
#include "../qmuparser/qmuparsererror.h"
|
#include "../qmuparser/qmuparsererror.h"
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
@ -65,7 +66,22 @@ QT_WARNING_POP
|
||||||
//---------------------------------------------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------------------------------------------
|
||||||
inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||||
{
|
{
|
||||||
Q_UNUSED(context)
|
// only the GUI thread should display message boxes. If you are
|
||||||
|
// writing a multithreaded application and the error happens on
|
||||||
|
// a non-GUI thread, you'll have to queue the message to the GUI
|
||||||
|
QCoreApplication *instance = QCoreApplication::instance();
|
||||||
|
const bool isGuiThread = instance && (QThread::currentThread() == instance->thread());
|
||||||
|
|
||||||
|
if (not isGuiThread)
|
||||||
|
{
|
||||||
|
auto Handler = [](QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||||
|
{
|
||||||
|
noisyFailureMsgHandler(type, context, msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
q_dispatch_async_main(Handler, type, context, msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Why on earth didn't Qt want to make failed signal/slot connections qWarning?
|
// Why on earth didn't Qt want to make failed signal/slot connections qWarning?
|
||||||
if ((type == QtDebugMsg) && msg.contains(QStringLiteral("::connect")))
|
if ((type == QtDebugMsg) && msg.contains(QStringLiteral("::connect")))
|
||||||
|
@ -130,12 +146,6 @@ inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &con
|
||||||
type = QtDebugMsg;
|
type = QtDebugMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
// only the GUI thread should display message boxes. If you are
|
|
||||||
// writing a multithreaded application and the error happens on
|
|
||||||
// a non-GUI thread, you'll have to queue the message to the GUI
|
|
||||||
QCoreApplication *instance = QCoreApplication::instance();
|
|
||||||
const bool isGuiThread = instance && (QThread::currentThread() == instance->thread());
|
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case QtDebugMsg:
|
case QtDebugMsg:
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include "../vmisc/vmath.h"
|
#include "../vmisc/vmath.h"
|
||||||
#include "../qmuparser/qmuparsererror.h"
|
#include "../qmuparser/qmuparsererror.h"
|
||||||
#include "../mainwindow.h"
|
#include "../mainwindow.h"
|
||||||
|
#include "../vmisc/qt_dispatch/qt_dispatch.h"
|
||||||
|
|
||||||
#include <QtDebug>
|
#include <QtDebug>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
@ -65,6 +66,23 @@ Q_DECL_CONSTEXPR auto DAYS_TO_KEEP_LOGS = 3;
|
||||||
//---------------------------------------------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------------------------------------------
|
||||||
inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||||
{
|
{
|
||||||
|
// only the GUI thread should display message boxes. If you are
|
||||||
|
// writing a multithreaded application and the error happens on
|
||||||
|
// a non-GUI thread, you'll have to queue the message to the GUI
|
||||||
|
QCoreApplication *instance = QCoreApplication::instance();
|
||||||
|
const bool isGuiThread = instance && (QThread::currentThread() == instance->thread());
|
||||||
|
|
||||||
|
if (not isGuiThread)
|
||||||
|
{
|
||||||
|
auto Handler = [](QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||||
|
{
|
||||||
|
noisyFailureMsgHandler(type, context, msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
q_dispatch_async_main(Handler, type, context, msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Why on earth didn't Qt want to make failed signal/slot connections qWarning?
|
// Why on earth didn't Qt want to make failed signal/slot connections qWarning?
|
||||||
if ((type == QtDebugMsg) && msg.contains(QStringLiteral("::connect")))
|
if ((type == QtDebugMsg) && msg.contains(QStringLiteral("::connect")))
|
||||||
{
|
{
|
||||||
|
@ -128,12 +146,6 @@ inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &con
|
||||||
type = QtDebugMsg;
|
type = QtDebugMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
// only the GUI thread should display message boxes. If you are
|
|
||||||
// writing a multithreaded application and the error happens on
|
|
||||||
// a non-GUI thread, you'll have to queue the message to the GUI
|
|
||||||
QCoreApplication *instance = QCoreApplication::instance();
|
|
||||||
const bool isGuiThread = instance && (QThread::currentThread() == instance->thread());
|
|
||||||
|
|
||||||
{
|
{
|
||||||
QString debugdate = "[" + QDateTime::currentDateTime().toString(QStringLiteral("yyyy.MM.dd hh:mm:ss"));
|
QString debugdate = "[" + QDateTime::currentDateTime().toString(QStringLiteral("yyyy.MM.dd hh:mm:ss"));
|
||||||
|
|
||||||
|
|
20
src/libs/vmisc/qt_dispatch/LICENSE.txt
Normal file
20
src/libs/vmisc/qt_dispatch/LICENSE.txt
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 Recep ASLANTAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
78
src/libs/vmisc/qt_dispatch/README.md
Normal file
78
src/libs/vmisc/qt_dispatch/README.md
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
Qt-Dispatch
|
||||||
|
===========
|
||||||
|
|
||||||
|
Execute block on the main thread or specified thread with Qt framework.
|
||||||
|
|
||||||
|
No required SIGNAL/SLOTs, no required QObject::connect...
|
||||||
|
|
||||||
|
Use with functions or C++11 Lamdas
|
||||||
|
|
||||||
|
Functions
|
||||||
|
------------
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
static void q_dispatch_async_main(fnBlock block); // Execute on Main / UI thread.
|
||||||
|
static void q_dispatch_async(QThread* thread, fnBlock block); // Execute on some thread.
|
||||||
|
```
|
||||||
|
|
||||||
|
SAMPLE USAGE
|
||||||
|
------------
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
|
||||||
|
#include "qt_dispatch.h"
|
||||||
|
|
||||||
|
// Usage 1: Using with lambdas
|
||||||
|
|
||||||
|
// This function called from non-ui/background thread.
|
||||||
|
void MainWindow::handleSockData(void *obj) {
|
||||||
|
// This usage requires C++11
|
||||||
|
q_dispatch_async_main([&]() {
|
||||||
|
// Add widgets to window using ui object.
|
||||||
|
SomeWidget *widget = new SomeWidget();
|
||||||
|
ui->someVLayout->addWidget(widget);
|
||||||
|
|
||||||
|
// check the thread id
|
||||||
|
const bool isGuiThread = QThread::currentThread() ==
|
||||||
|
QCoreApplication::instance()->thread();
|
||||||
|
qWarning() << "isGuiThread: " << isGuiThread; // true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage 2: Using with functions
|
||||||
|
|
||||||
|
void MainWindow::handleSockData(void *obj{
|
||||||
|
void somefunc();
|
||||||
|
q_dispatch_async_main(somefunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void somefunc() {
|
||||||
|
const bool isGuiThread = QThread::currentThread() ==
|
||||||
|
QCoreApplication::instance()->thread();
|
||||||
|
qWarning() << "isGuiThread: " << isGuiThread; // true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Usage 3: Using with other threads
|
||||||
|
|
||||||
|
void MainWindow::on_action1_triggered() {
|
||||||
|
QThread *newThread = new QThread();
|
||||||
|
newThread->start();
|
||||||
|
|
||||||
|
q_dispatch_async(newThread, []() {
|
||||||
|
const bool isGuiThread = QThread::currentThread() ==
|
||||||
|
QCoreApplication::instance()->thread();
|
||||||
|
qWarning() << "isGuiThread: " << isGuiThread; // false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Enabling C++11 Compiler Support: Add these lines to your .pro file
|
||||||
|
|
||||||
|
```
|
||||||
|
CONFIG += c++11
|
||||||
|
QMAKE_CXXFLAGS += -std=c++11 -stdlib=libc++ -mmacosx-version-min=10.7
|
||||||
|
LIBS += -stdlib=libc++ -mmacosx-version-min=10.7
|
||||||
|
```
|
||||||
|
|
||||||
|
###Enjoy!
|
103
src/libs/vmisc/qt_dispatch/qt_dispatch.h
Normal file
103
src/libs/vmisc/qt_dispatch/qt_dispatch.h
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
//
|
||||||
|
// Created by Recep ASLANTAS
|
||||||
|
// Copyright (c) 2013 Recep ASLANTAS. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef THREAD_DISPATCHER_H
|
||||||
|
#define THREAD_DISPATCHER_H
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
#include <QMetaObject>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
typedef std::function<void()> voidBlock;
|
||||||
|
|
||||||
|
class WorkerClass : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
WorkerClass(QThread *thread)
|
||||||
|
{
|
||||||
|
moveToThread(thread);
|
||||||
|
connect(QThread::currentThread(), &QThread::finished, this, &WorkerClass::deleteLater);
|
||||||
|
}
|
||||||
|
public slots:
|
||||||
|
void DoWork(voidBlock block)
|
||||||
|
{
|
||||||
|
block();
|
||||||
|
deleteLater();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void q_dispatch_async(QThread* thread, voidBlock block)
|
||||||
|
{
|
||||||
|
qRegisterMetaType<voidBlock>("voidBlock");
|
||||||
|
|
||||||
|
WorkerClass *worker = new WorkerClass(thread);
|
||||||
|
QMetaObject::invokeMethod(worker, "DoWork", Qt::QueuedConnection, Q_ARG(voidBlock, block));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void q_dispatch_async_main(voidBlock block)
|
||||||
|
{
|
||||||
|
QThread *mainThread = QCoreApplication::instance()->thread();
|
||||||
|
q_dispatch_async(mainThread, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef std::function<void(QtMsgType, const QMessageLogContext &, const QString &)> msgHandlerBlock;
|
||||||
|
|
||||||
|
class MsgHandlerWorkerClass : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MsgHandlerWorkerClass(QThread *thread, QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||||
|
: m_type(type),
|
||||||
|
m_msg(msg),
|
||||||
|
m_line(context.line),
|
||||||
|
m_file(context.file),
|
||||||
|
m_function(context.function),
|
||||||
|
m_category(context.category)
|
||||||
|
{
|
||||||
|
moveToThread(thread);
|
||||||
|
connect(QThread::currentThread(), &QThread::finished, this, &WorkerClass::deleteLater);
|
||||||
|
}
|
||||||
|
public slots:
|
||||||
|
void DoWork(msgHandlerBlock block)
|
||||||
|
{
|
||||||
|
block(m_type, QMessageLogContext(qUtf8Printable(m_file), m_line, qUtf8Printable(m_function),
|
||||||
|
qUtf8Printable(m_category)), m_msg);
|
||||||
|
deleteLater();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
QtMsgType m_type;
|
||||||
|
QString m_msg;
|
||||||
|
|
||||||
|
// We cannot make copy of QMessageLogContext. So, we must save its data instead and recreate it later.
|
||||||
|
int m_line;
|
||||||
|
QString m_file;
|
||||||
|
QString m_function;
|
||||||
|
QString m_category;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void q_dispatch_async(QThread* thread, msgHandlerBlock block, QtMsgType type, const QMessageLogContext &context,
|
||||||
|
const QString &msg)
|
||||||
|
{
|
||||||
|
qRegisterMetaType<msgHandlerBlock>("msgHandlerBlock");
|
||||||
|
|
||||||
|
MsgHandlerWorkerClass *worker = new MsgHandlerWorkerClass(thread, type, context, msg);
|
||||||
|
QMetaObject::invokeMethod(worker, "DoWork", Qt::QueuedConnection, Q_ARG(msgHandlerBlock, block));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void q_dispatch_async_main(msgHandlerBlock block, QtMsgType type, const QMessageLogContext &context,
|
||||||
|
const QString &msg)
|
||||||
|
{
|
||||||
|
QThread *mainThread = QCoreApplication::instance()->thread();
|
||||||
|
q_dispatch_async(mainThread, block, type, context, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // THREAD_DISPATCHER_H
|
|
@ -38,7 +38,8 @@ HEADERS += \
|
||||||
$$PWD/defglobal.h \
|
$$PWD/defglobal.h \
|
||||||
$$PWD/backport/qoverload.h \
|
$$PWD/backport/qoverload.h \
|
||||||
$$PWD/testvapplication.h \
|
$$PWD/testvapplication.h \
|
||||||
$$PWD/literals.h
|
$$PWD/literals.h \
|
||||||
|
$$PWD/qt_dispatch/qt_dispatch.h
|
||||||
|
|
||||||
# Qt's versions
|
# Qt's versions
|
||||||
# 5.2.0, 5.2.1
|
# 5.2.0, 5.2.1
|
||||||
|
|
Loading…
Reference in New Issue
Block a user