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/vsysexits.h"
|
||||
#include "../vmisc/diagnostic.h"
|
||||
#include "../vmisc/qt_dispatch/qt_dispatch.h"
|
||||
#include "../qmuparser/qmuparsererror.h"
|
||||
|
||||
#include <QDir>
|
||||
|
@ -65,7 +66,22 @@ QT_WARNING_POP
|
|||
//---------------------------------------------------------------------------------------------------------------------
|
||||
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?
|
||||
if ((type == QtDebugMsg) && msg.contains(QStringLiteral("::connect")))
|
||||
|
@ -130,12 +146,6 @@ inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &con
|
|||
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)
|
||||
{
|
||||
case QtDebugMsg:
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "../vmisc/vmath.h"
|
||||
#include "../qmuparser/qmuparsererror.h"
|
||||
#include "../mainwindow.h"
|
||||
#include "../vmisc/qt_dispatch/qt_dispatch.h"
|
||||
|
||||
#include <QtDebug>
|
||||
#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)
|
||||
{
|
||||
// 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?
|
||||
if ((type == QtDebugMsg) && msg.contains(QStringLiteral("::connect")))
|
||||
{
|
||||
|
@ -128,12 +146,6 @@ inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &con
|
|||
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"));
|
||||
|
||||
|
|
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/backport/qoverload.h \
|
||||
$$PWD/testvapplication.h \
|
||||
$$PWD/literals.h
|
||||
$$PWD/literals.h \
|
||||
$$PWD/qt_dispatch/qt_dispatch.h
|
||||
|
||||
# Qt's versions
|
||||
# 5.2.0, 5.2.1
|
||||
|
|
Loading…
Reference in New Issue
Block a user