多线程加载大文件到QTextEdit中我要加载一个大文件到QTextEdit中,就是在其他线程打开文件读取,然后写入到Q
多线程加载大文件到QTextEdit中
我要加载一个大文件到QTextEdit中,
就是在其他线程打开文件读取,然后写入到QTextEdit中去。
这样就不会导致main GUI线程无法响应。
1. 但是其他线程不能调用GUI元素啊。有什么好的办法吗?谢谢。
2. 我现在是想用readLine,这样一行一行读入,然后QTextEdit::append这样写入的。这样是否合适?谢谢
[最优解释]
readall不可以吗?一次读进QString。
[其他解释]个人感觉readAll会触发构造多个QString对象
每个QString对象在capacity()不够时 会重新resize 然后拷贝原来的数据 相当于构造了多次
对几十K的数据应该没问题 上10M的估计会多次resize
[其他解释]其实读文件不要很多时间,完全是显示费时间,可以具体测试一下读文件用多少时间(读取到一个QString里)。
[其他解释]class workThread: public QThread
{
Q_OBJECT
signals:
void signal_read_done(QByteArray & data);
private:
void run();
}
新创建一个workThread,在workThread里读取大文件。读取完毕的时候 emit signal_read_done,然后在界面线程里做slot,读取数据
[其他解释]我也想过这个样子。但是对于几十M的文件,这样读取我不知道QString会不会有问题。
[其他解释]别用Qstring
就用QFile read
然后按需读取
你这样相当于把所有的文件全部加载进来 内存占的肯定比较大 而且估计QString不适合放这么多数据吧
这意味着QString每次当capacity不够时 要重新resize 然后拷贝原来的数据
[其他解释]我现在就是一行一行读取的啊。
我做在其他线程,就无法调用QTextEdit来插入了。
[其他解释]之前已经说了
你在其他线程处理完了 然后产生响应的事件 在QTextEdit中捕获这个signal 驱动响应的slot完成所有的操作
你不要直接在QTextEdit里操作 实际上非界面线程直接操作 GUI是非线程安全的
用signal - slot来完成
[其他解释]还是开始的话:读文件不是问题,关键是显示慢。
[其他解释]有可能
显示可以做增量显示
读出来1MB 就显示一次
[其他解释]刚测试完,结果:一个2376KB的文本文件,读取用80ms,显示出来用2150ms。
如果只是显示无格式文本,用QPlainTextEdit能明显加快。
[其他解释]不错
学习了
[其他解释]那这样的话只能读取1M到QString中,然后发射信号到slot中由main线程去插入到textedit中去。
是吧?
[其他解释]QFile
先read 1M数据
然后seek到1M
emit 这1M的数据到QTextEdit显示
再read 1M
seek到2M
emit 1M显示
[其他解释]恩,谢谢。
[其他解释]我觉得这存在一个同步的问题,读取文件块,然而显示到gui中却很慢。
那么这样子会导致读取文件线程一直在等待主线程。
另外,QString这个类那应该new出来吧,不然栈区能存储得了吗?
谢谢
[其他解释]我这些文件非常大。你说用readAll,这样一次性加载那么多数据到内存中,会占用很大的。
我想用楼上那兄弟说得在其他线程中一次读取1M,这样一次一次的读取。
在workThread的run中进行同步,用QMutex。
[其他解释]其实中间用QString完全是多余,只是为了测试。
完全可以readall,一次读进来放进edit。
我测试了:读文件用80ms,显示出来用2150ms(函数运行完了),其实还没看见窗口,还要一会窗口才会出来。
显示大文件的edit基本上都是自己实现的。
[其他解释]恩,不过分段显示没坐过这个。你做过吗?
应该很麻烦吧。
[其他解释]还是自己实现edit吧,现成的控件满足不了要求。
不管一次读取多少文本,最后呢?如果不是自己实现edit分段显示,还不是全部文本放进内存里了。
[其他解释]我试了试
jdwx1说的很对 显示慢 读还行 速度不是瓶颈
我用下面的方法写了写 UI基本上不会卡 反正可以看到QTextEdit一直在加载数据
我每次读进来的数据用signal传出去 用Qt的signal-slot做了数据拷贝 因此读完之后直接释放了
如果不用界面显示的话 内存很小 大概4,5M左右
用界面显示的话 内存就比较大了
我这里没有写同步的机制 其实你可以
两个线程互相控制
readThread 读完了通知主界面显示
主界面显示完了 通知readThread 继续读
我这里sleep了 读个20M的文本 不会卡死界面
void
readThread::run()
{
QFileInfo info(mFileURL);
if ( false == info.exists())
{
qDebug()<<"file not exist";
return ;
}
qint64 fileSize = info.size();
QFile theFile(mFileURL);
if (!theFile.open(QIODevice::ReadOnly))
{
return;
}
if ( fileSize < 1024 * 1024 ) //小于1M就全部读取
{
QByteArray & buffer = theFile.readAll();
qDebug("readThread::run %p\n",&buffer);
emit signal_buffer_read(buffer);
buffer.clear();
theFile.close();
}
else
{
int readNumberCnt = 0;
int loopNumber = fileSize /( 64 * 1024);
int leftSize = fileSize % ( 64 * 1024);
for ( int i = 0 ; i < loopNumber ; i ++ )
{
QByteArray & buffer = theFile.read(64*1024);
emit signal_buffer_read(buffer);
Q_ASSERT( true == theFile.seek(64*1024* (i+1)));
readNumberCnt += buffer.size();
qDebug()<< "readThread "<<theFile.pos();
QThread::msleep(200);
buffer.clear();
}
if ( leftSize )
{
QByteArray & buffer = theFile.read(leftSize);
qDebug("readThread::run %p\n",&buffer);
emit signal_buffer_read(buffer);
readNumberCnt += buffer.size();
buffer.clear();
}
Q_ASSERT(readNumberCnt == fileSize );
theFile.close();
}
}
[其他解释]
也没做过,不过可以考虑找一个开源的edit看看源码(scite)。
或者找一个开源的edit控件。
[其他解释]
非常感谢你。我考虑的是若是100M的文件,那么单纯QTextEdit加载显示进去就要100M来存储那些字符。
所以只能做分段显示。
另外,你的代码sleep这样子行的通吗?你不能确定他能在200毫秒里面显示完啊。
所以我觉得用QWaitCondition来做,当gui显示完了,就QWaitCondition.wakeAll(),读取线程发送完信号后就wait,等待gui线程的wakeAll。
这样子应该没问题吧。
[其他解释]做显示的难点在于按需显示
你可以看看word 启动时 不是所有的数据都显示
而是到当前界面时 再加载数据
写一个完备的edit难度很大
你每次读64K 然后sleep一下 基本不会卡死界面
[其他解释]用到100M是我猜的,我没试。我意思是:读100M数据显示到QTextEdit中,那么QTextEdit肯定最少也的要100M内存啊。
[其他解释]恩 我前面的同步写的比较简单 直接用sleep来的 你可以考虑用wait来同步
我如果不在界面显示 只在界面线程答应buffer 读12M的数据 最后运行完了 大概只占用4MB
说明读的地方没内存泄露
但是要在界面显示 内存居然到了100多MB 所以我觉得QTextedit在后台做了很多数据拷贝
[其他解释]其实我没懂你的意思,是要加载一个大文件GUI不被卡住,还是要想某些成品的编辑器一样能处理任意大的文本?不过像是第一个要求。
[其他解释]还是要麻烦你帮忙看下代码,我搞运维的所以qt不怎么懂。
下面是刚写的线程类。我觉得还是有些问题。
1. 你读取64kb的话,那么存在可能将一个字符截断。因为在QTextEdit是用append的,那么他会自动加一个换行符,这样就将一个字符给显示错了。
2. 下面这小段是我在gui中的,若用户点击关闭按钮则调用thread中的stop函数,然后等待thread的结束,
这样写有没有问题呢?我在thread的stop中直接调用terminal的,这个没问题吧?
谢谢
void MainWindow::closeEvent(QCloseEvent *event)
{
if(myThread->isRunning())
myThread->stop();
myThread->wait();
event->accept();
}
---------------------------------------------
class MyThread:public QThread
{
Q_OBJECT
public:
MyThread(QObject *parent=0):QThread(parent)
{
}
void setParams(const QString *PfilePath,QByteArray *PreadData,QWaitCondition *PreadWait,QMutex *Pmutex)
{
filePath = *PfilePath;
readData = PreadData;
readWait = PreadWait;
mutex = Pmutex;
}
void stop()
{
mutex->unlock();
this->terminate();
}
signals:
void loadOk();
private:
QString filePath;
QByteArray *readData;
QWaitCondition *readWait;
QMutex *mutex;
protected:
void run()
{
QFileInfo info(filePath);
if( false == info.exists() )
return;
qint64 fileSize = info.size();
QFile file(filePath);
if(!file.open(QFile::ReadOnly))
return;
if(fileSize < 1024*1024 )
{
*readData = file.readAll();
emit loadOk();
} else {
int loop = fileSize /(64*1024);
int leftSize = fileSize%(64*1024);
for(int i=0;i<loop;i++)
{
mutex->lock();
*readData = file.read(64*1024);
emit loadOk();
readWait->wait(mutex);
mutex->unlock();
}
if(leftSize)
{
*readData = file.read(leftSize);
emit loadOk();
}
}
file.close();
}
};
[其他解释]
1. 你读取64kb的话,那么存在可能将一个字符截断。因为在QTextEdit是用append的,那么他会自动加一个换行符,这样就将一个字符给显示错了。
这是我搞错了。不会截断。呵呵
[其他解释]gui不卡主
[其他解释]是啊。不断的滚动。我看了下QTextEdit源码,append数据后,他会将滚动条setValue到最后面。
还有个问题请教下。
我QMessageBox::about(this,"","");
我在MainWindow中设置了字体。为什么QMessageBox却不会继承这个字体呢?我将this传入了QMessageBox啊。
[其他解释]刚才试了一下,实在是太慢了,一次读一行,45k的文件要11秒,确实卡不住了,屏幕上文本在不停的滚动。
[其他解释]据我知道的,这个this是确定子窗口(QMessageBox)的位置用的,有this,QMessageBox出现在parent的中间,
没有传this,QMessageBox显示在屏幕的中间。