![](/img/trans.png)
[英]Is it possible to force execute main thread (GUI thread) from render thread?
[英]How to force Qt to update GUI from not main thread
我上周因为QPlainTextEdit更新引起的问题而烦恼。 我正试图在QMainWindow对话窗口中创建单独的QPlainTextEdit。 当我尝试使用appendHtml信号(也尝试使用appendText)时,问题就开始了,除非用鼠标标记,否则放置的文本是不可见的。 重新绘制或更新程序崩溃或无可见操作的原因。
使用QPlainTextEdit标头简化QDialog的代码:
namespace Ui {
class LogWindow;
}
class LogWriter: public QDialog
{
Q_OBJECT
QMutex print_lock;
public:
class Log{
Q_OBJECT
const static int MAX_SIZE = 100;
bool to_terminal;
QString color;
QMutex *print_lock;
QPlainTextEdit *text_place;
QVector< QPair<QString,time_t> > history;
LogWriter * obj;
public:
bool print;
Log(bool _print,QString _color,LogWriter *obj_ = NULL)
{print = _print; color = _color; obj = obj_;}
void setLock(QMutex *print_lock_){print_lock = print_lock_;}
void setTextField(QPlainTextEdit *_text) {text_place = _text;}
Log& operator<<(QString &a);
Log& operator<<(const char* a);
};
static LogWriter* getInstance()
{
static LogWriter instance; // Guaranteed to be destroyed.
// Instantiated on first use.
return &instance;
}
~LogWriter();
Log LOW,MEDIUM,HIGH;
Ui::LogWindow *ui;
signals:
void signalLogAppend(QString);
};
简化的方法定义代码:
LogWriter::LogWriter(QWidget * parent): QDialog(parent) {
ui = new Ui::LogWindow;
ui->setupUi(this);
LOW.setLock(&print_lock);
MEDIUM.setLock(&print_lock);
HIGH.setLock(&print_lock);
connect(this,SIGNAL(signalLogAppend(QString)),ui->plainTextEdit,
SLOT(appendHtml(QString)),Qt::DirectConnection);
}
LogWriter::Log& LogWriter::Log::operator<< (QString &s){
history.push_front(qMakePair(s,time(NULL)));
if(history.size() > MAX_SIZE) history.pop_back();
if(print){
//print_lock->lock();
QString text = "<font color=\"";
text += color + "\">";
text += s + "</font>";
//cout << text.toStdString() << endl;
//text_place->appendHtml(text);
//text_place->repaint();
emit (obj)->signalLogAppend(text);
//print_lock->unlock();
}
return *this;
}
我有两个单独的UI文件(第一个用于主窗口,第二个用于日志窗口)。 我必须在整个程序中使用日志窗口(大约10个线程),我坚持这个问题。 我的问题是 - 是否可以在不使用主线程的情况下强制进行GUI更新,如果不是 - 我还有其他可能性。 如果可能的话,我宁愿避免重构我的所有代码 - 这需要一些时间来完成它。 现在日志记录非常简单 - 我需要:
LogWindow *log = LogWindow::getInstance();
log->MEDIUM << "something";
作为附加信息,我添加了QTCreator警告:
QObject::connect: Cannot queue arguments of type 'QTextBlock'
(Make sure 'QTextBlock' is registered using qRegisterMetaType().)
QObject::connect: Cannot queue arguments of type 'QTextCursor'
(Make sure 'QTextCursor' is registered using qRegisterMetaType().)
如果我正确理解您的代码,您正在尝试从后台线程登录并使用直接连接将信号传递给GUI线程? 这不会起作用,你必须通过默认连接发送信号,因此Qt可以发现它是一个跨线程信号并相应地通过线程传递它(即通过前台线程上的消息循环)。
在Qt中,任何GUI交互都必须发生在Main / foreground线程中,否则会发生坏事。 您当然可以从后台线程发送信号以触发GUI更新 - 我一直这样做 - 但您需要确保使用正确的连接。 直接连接导致直接函数调用,在这种情况下不适合您。
在您的代码中,问题是对connect()的调用 - 当您应该使用默认设置时,您明确指定信号到插槽连接的连接模式。 如果将连接显式设置为Qt::DirectConnection
,则底层代码将执行对指定插槽的直接调用,这意味着您最终在信号的线程上下文中调用插槽。 您不希望这样,因为信号是在后台线程中触发的。
您不能将任意类型/类传递给信号和插槽。 该列表是有限的,并非所有Qt类都在列表中。 要将类型/类添加到可以传递给信号/槽的列表中,必须为该类调用qRegisterMetaType。 我建议在你试图传递给类似信号的类的构造函数中调用它:
MyClass::MyClass() : MyParentClass()
{
static int reg = qRegisterMetaType<MyClass>("MyClass");
}
static int确保只能调用一次注册,然后才能使用任何MyClass实例。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.