• 24851阅读
  • 16回复

QThread子线程读取主线程界面中QTextEdit变量问题,写得很详细,但是应该很容易看明白的... [复制链接]

上一主题 下一主题
离线iiiyyyhhhsss
 

只看楼主 倒序阅读 楼主  发表于: 2010-03-22
— 本帖被 XChinux 从 General Qt Programming 移动到本区(2011-01-02) —
我把主界面的一个textEdit的指针传到新开的QThread线程里去

我子类化了QThread,然后在构造函数里加了一个参数,以便把指针传递进去,如

class MyThread: public QThread
{
......
QTextEdit* textEdit;                //在我子类里加了一个QTextEdit指针
}


MyThread::MyThread(QObject*parent,QTextEdit* textEdit_pass)          //构造函数
    :QThread(parent)
{....
this->textEdit=textEdit_pass;
}

  run()函数代码如下:

void MyThread::run()
{
     .........
     qDebug()<<textEdit->objectName();                     //没有问题
     qDebug()<<textEdit->toPlainText();                      //没有问题
//      qDebug()<<textEdit->setPlainText();                 //有问题
   ..........
}



出现了一个很奇怪的问题:


在线程里,我对textEdit读取了它的信息objectName(),toPlainText(),size()等都没有问题

但是,

当我尝试对textEdit进行修改的时候,如

setPlainText(),append(),resize()等操作的时候,程序运行到这些修改的操作时,就会出错崩溃

这是什么原因? 

警告报错信息:

Runtime Error!

Program:...thread.exe                 //thread.exe就是我的程序
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
[ 此帖被iiiyyyhhhsss在2010-03-22 09:16重新编辑 ]
离线dbzhang800

只看该作者 1楼 发表于: 2010-03-22
多看看manual
  1. Although QObject is reentrant, the GUI classes, notably QWidget and all its subclasses, are not reentrant. They can only be used from the main thread.
离线iiiyyyhhhsss

只看该作者 2楼 发表于: 2010-03-22
引用第1楼dbzhang800于2010-03-22 09:18发表的  :
多看看manual
  1. Although QObject is reentrant, the GUI classes, notably QWidget and all its subclasses, are not reentrant. They can only be used from the main thread.



好的,明白了,只是觉得奇怪,能读取信息, 却不能修改信息...

那么,如果要在子线程里对主线程界面进行操作,通常用什么方法呢?
离线iiiyyyhhhsss

只看该作者 3楼 发表于: 2010-03-22
是不是通常用这个方法:

转自:http://blog.chinaunix.net/u2/72991/showart_1999557.html

可以通过QThread实现跨平台的多线程开发,Qt库负责在特定平台上的特定多线程实现。要采用QThread进行多线程开发,首先需要包含头文件:
#include <QThread>
然后需要从QThread继承一个自己的线程类,暂作MyThread,关键是要实现QThread内的一个虚函数run。在run函数内部,最后一般都调用exec(),使得线程进入事件循环,否则线程主函数run运行结束,线程也就退出了。
class MyThread : public QThread
{
public:
   MyThread();
public:
   Dialog *parent;
public:
   void run();
};
其中,parent存储的是创建该线程的窗口,也就是该线程的父线程,主要用于该子线程向父线程发送特定事件,以便进行线程间通信。


自定义消息首先需要包含头文件:
#include <QEvent>
然后定义自己的事件值:
const QEvent::Type MyEvent = (QEvent::Type)1234;
然后在run函数里面(实际上是指在任何你想要和父线程进行通信的地方,我这里简化为就是在run函数内部了)通过:
QApplication::postEvent(parent, new QEvent(MyEvent));
将该事件发送出去。
父线程内通过改写虚函数event,来使得能够捕获消息:
bool Dialog::event(QEvent *event)
{
   if (event->type() == MyEvent)
   {
      QMessageBox::information(this, "INFORMATION", "This is son sented signal!");
      return true;
   }
   return QWidget::event(event);
}
这样,两个线程就能够同时运行并能够进行线程间通信了。
离线benbenmajia

只看该作者 4楼 发表于: 2010-03-22
......最近线程很热啊,也可以用信号,不过要排队的
安然.....
离线iiiyyyhhhsss

只看该作者 5楼 发表于: 2010-03-22
引用第4楼benbenmajia于2010-03-22 10:36发表的  :
......最近线程很热啊,也可以用信号,不过要排队的



那你有什么好建议?
离线benbenmajia

只看该作者 6楼 发表于: 2010-03-22
我那天看别人问的这个问题然后到网上搜索了一下,看法倒没有,1+1不是copy了吗?UI的更改只能在主线程中。我是说两个线程的通信时可以用信号的,也可以用时间,postEvent
安然.....
离线benbenmajia

只看该作者 7楼 发表于: 2010-03-22
事件不是有post和send吗?好像是要post的,google~~~google!~~~
安然.....
离线ooyoyo
只看该作者 8楼 发表于: 2010-03-22
应该可以在你的线程里,postEvent一个自定义的customEvent,然后在主线程的customEvent方法里,过滤是否是自己定义的Event,如果是就改写Edit内容
或者也可以定义一个信号,用connect把它和主线程某个槽关联起来
离线iiiyyyhhhsss

只看该作者 9楼 发表于: 2010-03-22
我已经成功用信号槽的方法往主线程里发送了数据,并且在主界面上显示出来

接下来想尝试用event的方法...

但想知道,   event方法  和  signal-slot方法   ,哪个更好呢?

各自的优缺点?
离线wato
只看该作者 10楼 发表于: 2010-03-22
引用第4楼benbenmajia于2010-03-22 10:36发表的  :
......最近线程很热啊,也可以用信号,不过要排队的


相对于事件的方法,信号槽的方法是同步的;当然,是相对而言啦
离线iiiyyyhhhsss

只看该作者 11楼 发表于: 2010-03-22
引用第10楼wato于2010-03-22 11:45发表的  :
相对于事件的方法,信号槽的方法是同步的;当然,是相对而言啦



那么事件就是异步了?
离线carllu
只看该作者 12楼 发表于: 2010-03-31
我今天在写一个线程修改界面上 QTextEdit,也遇到奇怪问题。我通过 void append( const QString &) 添加内容,在 Debug 模式下,会报错误,换到 Release 下就正常。
有些害怕将来突然之间,又不行了。

错误信息:
ASSERT: "qApp && qApp->thread() == QThread::currentThread()" in file kernel\qapplication_win.cpp, line 905

我的系统:
windows xp sp2;
vs 2008 + sp1
QT V4.6.2
离线jialeer123
只看该作者 13楼 发表于: 2011-03-23
QThread 线程编译出现错误变量问题,
g++ -c -pipe -DQWS -fno-exceptions -fno-rtti -Wall -W -O2 -fno-default-inline -DNO_DEBUG -DQT_THREAD_SUPPORT -I/usr/local/arm/QT/qt-2.3.7-cpu/include -o aplayer.o aplayer.cpp
g++ -c -pipe -DQWS -fno-exceptions -fno-rtti -Wall -W -O2 -fno-default-inline -DNO_DEBUG -DQT_THREAD_SUPPORT -I/usr/local/arm/QT/qt-2.3.7-cpu/include -o main.o
main.cpp
/usr/local/arm/QT/qt-2.3.7-cpu/bin/moc aplayer.h -o moc_aplayer.cpp
g++ -c -pipe -DQWS -fno-exceptions -fno-rtti -Wall -W -O2 -fno-default-inline -DNO_DEBUG -DQT_THREAD_SUPPORT -I/usr/local/arm/QT/qt-2.3.7-cpu/include -o moc_aplayer.o moc_aplayer.cpp
moc_aplayer.cpp: In member function `void Mythread::initMetaObject()':
moc_aplayer.cpp:176: no method `QThread::className'
moc_aplayer.cpp:177: `badSuperclassWarning' undeclared (first use this
   function)
moc_aplayer.cpp:177: (Each undeclared identifier is reported only once for each
   function it appears in.)
moc_aplayer.cpp: In static member function `static QMetaObject*
   Mythread::staticMetaObject()':
moc_aplayer.cpp:199: no method `QThread::staticMetaObject'
make: *** [moc_aplayer.o] Error 1
离线wjlsmail

只看该作者 14楼 发表于: 2011-03-28
用信号和事件都可以。
离线voidbroken

只看该作者 15楼 发表于: 2011-03-30
1.   信号是较为高级的机制,事件是较为低级的机制。一般情况下,事件来自底层窗口系统,但是也有可能通过QApplication类的QApplication::sendEvent()和QApplication::postEvent()来手动发送事件。
2.   信号-槽中,信号触发多个槽是没有固定顺序的,会全部触发。(观察者模式)
事件触发的多个处理者是有固定的顺序的,优先级高的决定是否把事件继续传递下去(通过返回值)。(职责链模式)
3.   都可以用于跨线程环境,只不过信号-槽要采用“队列连接”或“自动连接(默认)”方式,不能采用“直接连接”(类似回调)。
4.   QT手册第7章开头指出:使用信号-槽机制与事件机制的一个根本原则是,在使用一个组件时,使用信号;在实现一个组件时,使用事件机制。原因是:事件机制是更为底层的机制(也就是某些情况下没有相应信号可以接收),例如鼠标在组件上的划过,按下和抬起,在处理这类事件时,可以决定何时发出类似clicked()的信号。
5.QCoreApplication::postEvent(),可以给任何线程中的任何对象投递一个事件,事件会在那个创建了对象的线程中通过事件循环派发。事件过滤器在所有线程中也被支持,不过它限定被监视对象与监视对象生存在同一线程中。类似地,QCoreApplication::sendEvent(),仅用于在调用此函数的线程中向目标对象投递事件。
离线voidbroken

只看该作者 16楼 发表于: 2011-03-30
仔细来看,事件与信号其实并无多大差别,从我们对其需求上来说,都只要能注册事件或信号响应函数,在事件或信号产生时能够被通知到即可。但有一项区别在于,事件处理函数的返回值是有意义的,我们要根据这个返回值来确定是否还要继续事件的处理,比如在QT中,事件处理函数如果返回true,则这个事件处理已完成,QApplication会接着处理下一个事件,而如果返回false,那么事件分派函数会继续向上寻找下一个可以处理该事件的注册方法。信号处理函数的返回值对信号分派器来说是无意义的。
另外还有一个需要我们关注的问题是事件和信号处理时的优先级问题。在QT中,事件因为都是与窗口相关的,所以事件回调时都是从当前窗口开始,一级一级向上派发,直到有一个窗口返回true,截断了事件的处理为止。对于信号的处理则比较简单,默认是没有顺序的,如果需要明确的顺序,可以在信号注册时显示地指明槽的位置。
在QT中,事件使用了一个事件队列来维护,如果事件的处理中又产生了新的事件,那么新的事件会加入到队列尾,直到当前事件处理完毕后, QApplication再去队列头取下一个事件来处理。而信号的处理方式有些不同,信号处理是立即回调的,也就是一个信号产生后,他上面所注册的所有槽都会立即被回调。这样就会产生一个递归调用的问题,比如某个信号处理器中又产生了一个信号,会使得信号的处理像一棵树一样的展开。
快速回复
限100 字节
 
上一个 下一个