在用QTreeWidget实现目录树时,点击目录,逐级展开需要一个加载对话框,表明正在加载数据,此时主线程一直在for循环里添加下一级目录的子节点,导致没有工作线程去执行加载对话框的paintEvent的绘制,就没有加载效果出现。
如下程序代码:
在for循环里,添加 QCoreApplication::processEvents();//添加处理事件
每添加一次节点,就调 QCoreApplication::processEvents();去处理其他需要响应的事件,就如单核CPU,多线程正在使用时,每个线程只使用一段CPU的时间片,时间一到就会被切出去,而这里是通过事件消息循环,去处理其他事件的消息
void CLocalDirTreeWgt::UpdateLeftTree(QTreeWidgetItem *item)
{
m_bLeftChange = true;
Qt::CheckState cs = item->checkState(0);
Qt::CheckState checkstate;
FILE_NODE iteminfo = item->data(0, FILE_TYPE).value<FILE_NODE>();
m_path = iteminfo.qFilePath;
/*添加path路径文件*/
QDir dir(m_path); //遍历各级子目录
if (ShowHiddenFolderAndFile() && ShowHiddenProtectedOSSystemFile())
{
dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System); //先过滤目录和文件
}
else if (ShowHiddenFolderAndFile())
{
dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden); //先过滤目录和文件
}
else if (ShowHiddenProtectedOSSystemFile())
{
dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot | QDir::System); //先过滤目录和文件
}
else
{
dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); //先过滤目录和文件
}
dir.setSorting(QDir::DirsFirst);//优先显示目录
QFileInfoList folder_list = dir.entryInfoList(); //获取当前所有目录
FILE_NODE info;
for (int i = 0; i != folder_list.size(); i++) //自动递归添加各目录到上一级目录
{
QCoreApplication::processEvents();//添加处理事件
QFileInfo folderinfo = folder_list.at(i);
if (folderinfo.isDir() && !folderinfo.isSymLink())
{
QString namepath = folder_list.at(i).absoluteFilePath(); //获取路径
QString name = folderinfo.fileName(); //获取目录名
if (ChildWidgetItemIsCombinedTree(item, namepath))
{
continue;
}
info.bFolder = true;
info.qFilePath = namepath;
QVariant var;
var.setValue(info);
QTreeWidgetItem* childDirItem = new QTreeWidgetItem(QStringList() << name);
QIcon icon = GetFolderFileIcon(namepath);
childDirItem->setIcon(0, icon);
childDirItem->setText(0, name);
childDirItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
childDirItem->setData(0, FILE_TYPE, var);
if (cs == Qt::PartiallyChecked)
{
//checkstate = GetCheckStateFormLeftTree(namepath);
//获取当前项item是否绑定了节点数据
SELECT_TREE_NODE *selectItemNode = item->data(0, NODE_PTR).value<SELECT_TREE_NODE*>();
if (NULL != selectItemNode)
{
checkstate = GetWidgetItemState(selectItemNode, childDirItem);
}
childDirItem->setCheckState(0, checkstate);
}
else
{
childDirItem->setCheckState(0, cs);
}
item->addChild(childDirItem); //将当前目录添加成path的子项
QString qstrpath = info.qFilePath;
qstrpath.replace("/", "\\");
bool bHaveSubDir = isHaveSubDir(qstrpath);
if (bHaveSubDir)
{
QTreeWidgetItem * childChildItem = new QTreeWidgetItem;
childChildItem->setText(0, FAKER_ITEM_TEXT);
childDirItem->addChild(childChildItem);
}
}
else
{
INT64 filesize = folderinfo.size();
QString name2 = folderinfo.fileName();
if (ChildWidgetItemIsCombinedTree(item, folderinfo.absoluteFilePath()))
{
continue;
}
info.bFolder = false;
info.qFilePath = folderinfo.absoluteFilePath();
QVariant var;
var.setValue(info);
QString filePath = folderinfo.filePath();
filePath.replace("/", "\\");
QIcon icon = GetFolderFileIcon(filePath);
QTreeWidgetItem *fileWgtItem = new QTreeWidgetItem();
fileWgtItem->setCheckState(0, Qt::Unchecked); //初始状态没有被选中
fileWgtItem->setIcon(0, icon);
fileWgtItem->setText(0, name2);
fileWgtItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); //设置树形控件子项的属性
fileWgtItem->setData(0, FILE_TYPE, var);
if (cs == Qt::PartiallyChecked)
{
//获取当前项item是否绑定了节点数据
SELECT_TREE_NODE *selectItemNode = item->data(0, NODE_PTR).value<SELECT_TREE_NODE*>();
if (NULL != selectItemNode)
{
checkstate = GetWidgetItemState(selectItemNode, fileWgtItem);
}
fileWgtItem->setCheckState(0, checkstate);
}
else
{
fileWgtItem->setCheckState(0, cs);
}
item->addChild(fileWgtItem);
fileWgtItem->setHidden(true);
}
}
}
//事件处理函数
void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags)
{
QThreadData *data = QThreadData::current();
if (!data->hasEventDispatcher())
return;
data->eventDispatcher.load()->processEvents(flags);
}
bool QWindowsGuiEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
{
const QEventLoop::ProcessEventsFlags oldFlags = m_flags; //将原来的事件标记保留
m_flags = flags; //存放新的事件标记
const bool rc = QEventDispatcherWin32::processEvents(flags); //处理新的事件标记
m_flags = oldFlags; //处理完后返回原来的事件标记
return rc;
}
bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
Q_D(QEventDispatcherWin32);
if (!d->internalHwnd) {
createInternalHwnd();
wakeUp(); // trigger a call to sendPostedEvents()
}
d->interrupt = false;
emit awake();
bool canWait;
bool retVal = false;
bool seenWM_QT_SENDPOSTEDEVENTS = false;
bool needWM_QT_SENDPOSTEDEVENTS = false;
do {
DWORD waitRet = 0;
HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1];
QVarLengthArray<MSG> processedTimers;
while (!d->interrupt) {
DWORD nCount = d->winEventNotifierList.count();
Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);
MSG msg;
bool haveMessage;
if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
// process queued user input events
haveMessage = true;
msg = d->queuedUserInputEvents.takeFirst();
} else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {
// process queued socket events
haveMessage = true;
msg = d->queuedSocketEvents.takeFirst();
} else {
haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE);
if (haveMessage) {
if ((flags & QEventLoop::ExcludeUserInputEvents)
&& ((msg.message >= WM_KEYFIRST
&& msg.message <= WM_KEYLAST)
|| (msg.message >= WM_MOUSEFIRST
&& msg.message <= WM_MOUSELAST)
|| msg.message == WM_MOUSEWHEEL
|| msg.message == WM_MOUSEHWHEEL
|| msg.message == WM_TOUCH
#ifndef QT_NO_GESTURES
|| msg.message == WM_GESTURE
|| msg.message == WM_GESTURENOTIFY
#endif
|| msg.message == WM_CLOSE)) {
// queue user input events for later processing
d->queuedUserInputEvents.append(msg);
continue;
}
if ((flags & QEventLoop::ExcludeSocketNotifiers)
&& (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) {
// queue socket events for later processing
d->queuedSocketEvents.append(msg);
continue;
}
}
}
if (!haveMessage) {
// no message - check for signalled objects
for (int i=0; i<(int)nCount; i++)
pHandles[i] = d->winEventNotifierList.at(i)->handle();
waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE);
if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) {
// a new message has arrived, process it
continue;
}
}
if (haveMessage) {
// WinCE doesn't support hooks at all, so we have to call this by hand :(
if (!d->getMessageHook)
(void) qt_GetMessageHook(0, PM_REMOVE, (LPARAM) &msg);
if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) {
if (seenWM_QT_SENDPOSTEDEVENTS) {
// when calling processEvents() "manually", we only want to send posted
// events once
needWM_QT_SENDPOSTEDEVENTS = true;
continue;
}
seenWM_QT_SENDPOSTEDEVENTS = true;
} else if (msg.message == WM_TIMER) {
// avoid live-lock by keeping track of the timers we've already sent
bool found = false;
for (int i = 0; !found && i < processedTimers.count(); ++i) {
const MSG processed = processedTimers.constData()[i];
found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam);
}
if (found)
continue;
processedTimers.append(msg);
} else if (msg.message == WM_QUIT) {
if (QCoreApplication::instance())
QCoreApplication::instance()->quit();
return false;
}
//在这里过滤事件,翻译,派发消息,去响应其他事件
if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} else if (waitRet - WAIT_OBJECT_0 < nCount) {
d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));
} else {
// nothing todo so break
break;
}
retVal = true;
}
// still nothing - wait for message or signalled objects
canWait = (!retVal
&& !d->interrupt
&& (flags & QEventLoop::WaitForMoreEvents));
if (canWait) {
DWORD nCount = d->winEventNotifierList.count();
Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);
for (int i=0; i<(int)nCount; i++)
pHandles[i] = d->winEventNotifierList.at(i)->handle();
emit aboutToBlock();
waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
emit awake();
if (waitRet - WAIT_OBJECT_0 < nCount) {
d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));
retVal = true;
}
}
} while (canWait);
if (!seenWM_QT_SENDPOSTEDEVENTS && (flags & QEventLoop::EventLoopExec) == 0) {
// when called "manually", always send posted events
sendPostedEvents();
}
if (needWM_QT_SENDPOSTEDEVENTS)
PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0);
return retVal;
}
本文探讨了使用QTreeWidget实现实时目录树加载时的优化技巧。在主线程中,通过在for循环内调用QCoreApplication::processEvents(),有效地避免了UI阻塞,实现了平滑的加载体验。文章深入解析了事件处理机制,并提供了具体代码示例。

4807

被折叠的 条评论
为什么被折叠?



