简体   繁体   English

Qt GUI变得无响应,发出信号的速度过快

[英]Qt GUI becomes unresponsive emitting signals too fast

I've got a small chat client that stores all history in an sqlite database. 我有一个小型的聊天客户端,可将所有历史记录存储在sqlite数据库中。 When the user clicks on the history tab in my application my app fetches all the relevant history and displays it in a QWebView . 当用户单击我的应用程序中的“ history选项卡时,我的应用程序将获取所有相关的历史记录并将其显示在QWebView Im fetching from a background thread dbThread below and then sending signals to update the QWebView accordingly. 我从下面的后台线程dbThread获取,然后发送信号以相应地更新QWebView

This works fine, until the database grows. 在数据库增长之前,它可以正常工作。 When the database gets larger the app starts to almost crashes. 当数据库变大时,应用程序几乎崩溃。 GUI is unresponsive for a few seconds until everything is loaded (4-6 secs) depending on the database size. GUI会在几秒钟内无响应,直到所有内容加载完毕(4-6秒)为止,具体取决于database大小。

I've tried to add Qt::QueuedConnection on the signals and also like mentioned above I'm handling all database queries from a background thread . 我试图在信号上添加Qt::QueuedConnection ,并且也如上所述,我正在处理来自background thread所有database查询。

I'm guessing that I'm emitting signals too fast. 我猜我发出信号的速度太快了。 Any ideas how to solve this? 任何想法如何解决这个问题?

Signals 信号

connect(dbtrad, SIGNAL(addAllHistoryMessage(QString, QString, QString, QString, QString)), this, SLOT(addAllHistoryMessage(QString, QString, QString, QString, QString)), Qt::QueuedConnection);
connect(dbtrad, SIGNAL(addAllHistoryMessageInner(QString, QString, QString, QString, QString)), this, SLOT(addAllHistoryMessageInner(QString, QString, QString, QString, QString)), Qt::QueuedConnection);

Code that fetches history from the sqlite database: 从sqlite数据库获取历史记录的代码:

// Loads all local history
void dbThread::loadAllHistory(QString agentID, QString agentName) {
    bool ret = false;
    bool retInner = false;
    QString retVal = "";

    QDateTime dateTime = dateTime.currentDateTime();
    QString dateTimeForTodayCheck = dateTime.toString("yyyy-MM-dd");

    if (db.isOpen()) {
        QSqlQuery query(db);
        QSqlQuery queryInner(db);

        ret = query.exec(QString("SELECT channelID, sender, time, message from chatHistory WHERE sender != 'ServerMessage' AND channelID NOT LIKE '%Agent%' GROUP BY channelID order by time DESC;"));

        if (ret) {

            while (query.next()) {    
                QString channelID = query.value(0).toString();
                QString sender = query.value(1).toString();
                QString time = query.value(2).toString();
                QString msg = query.value(3).toString();


                QString timeStr;
                QString fmt = "yyyy-MM-dd hh:mm:ss";
                QDateTime dt = QDateTime::fromString(time, fmt);
                QDateTime dtCompare = QDateTime::fromString(time, fmt);

                if(dateTimeForTodayCheck == dtCompare.toString("yyyy-MM-dd")) { // If today
                    timeStr = "Today " + dt.toString("hh:mm");
                } else {
                    timeStr = dt.toString("dd MMM yyyy");
                }

                if(sender == agentID) {
                    sender = agentName;
                }
                // Grab all the tags
                QString tempTagsForChannelID = getHistoryTagsString(channelID);

                emit addAllHistoryMessage(channelID, sender, timeStr, msg, tempTagsForChannelID);

                // Load sub-history
                retInner = queryInner.exec(QString("SELECT * from chatHistory WHERE sender != 'ServerMessage' AND channelID = '%1' and message != '%2' order by time DESC;").arg(channelID).arg(msg));

                if (retInner) {
                    while (queryInner.next()) {
                        QString channelIDInner = queryInner.value(0).toString();
                        QString senderInner = queryInner.value(1).toString();
                        QString timeInner = queryInner.value(4).toString();
                        QString msgInner = queryInner.value(2).toString();

                        QString timeStr2;
                        QString fmt = "yyyy-MM-dd hh:mm:ss";
                        QDateTime dt = QDateTime::fromString(timeInner, fmt);
                        QDateTime dtCompare = QDateTime::fromString(timeInner, fmt);

                        if(dateTimeForTodayCheck == dtCompare.toString("yyyy-MM-dd")) { // If today
                            timeStr2 = "Today " + dt.toString("hh:mm");
                        } else {
                            timeStr2 = dt.toString("dd MMM yyyy");
                        }

                        if(senderInner == agentID) {
                            senderInner = agentName;
                        }

                        emit addAllHistoryMessageInner(channelIDInner, senderInner, timeStr2, msgInner, tempTagsForChannelID);
                    }
                }
            }
        }
    }
}

My code to update: 我要更新的代码:

void MainWindow::addAllHistoryMessageInner(QString channelIDInner, QString senderInner, QString timeStr2, QString msgInner, QString tempTagsForChannelID) {
    ui->webViewHistory->page()->mainFrame()->evaluateJavaScript("$('#history tbody').append('<tr id=\"" + channelIDInner+ "\" class=\"hiddenRow\"><td>" + senderInner + "</td><td align=\"center\">" + timeStr2 + "</td><td align=\"center\" style=\"word-wrap:break-word;\">" + msgInner.remove(QRegExp("<[^>]*>")) + "</td><td align=\"center\">" + tempTagsForChannelID + "</td></tr>');undefined");
}

void MainWindow::addAllHistoryMessage(QString channelID, QString sender, QString timeStr, QString msg, QString tempTagsForChannelID) {
    ui->webViewHistory->page()->mainFrame()->evaluateJavaScript("$('#history tbody').append('<tr id=\"" + channelID + "\"><td>" + sender + "</td><td align=\"center\">" + timeStr + "</td><td align=\"center\" style=\"word-wrap:break-word;\">" + msg.remove(QRegExp("<[^>]*>")) + "</td><td align=\"center\" style=\"word-wrap:break-word;\">" + tempTagsForChannelID + "</td></tr>');undefined");
}

Edit: Implementation of dbThread 编辑:dbThread的实现

thread = new QThread(this);
dbtrad = new dbThread();
dbtrad->moveToThread(thread);

Edit 2: This is how I call loadAllHistory 编辑2:这就是我所说的loadAllHistory

I create a signal: 我创建一个信号:

connect(this, SIGNAL(loadAllHistoryS(QString, QString)), dbtrad, SLOT(loadAllHistory(QString, QString)));

And call it like this: 并这样称呼它:

emit loadAllHistoryS(agentID, agentName);

The problem is, that your main thread is interrupted for every single row in the innerQuery . 问题在于,您的主线程会为innerQuery中的每一行中断。 This destroys the benefits of loading the data in separate thread. 这破坏了在单独的线程中加载数据的好处。 Probably the overhead of the signal/slot communication over thread boundaries is even higher than the costs of loading a single row from the database. 线程边界上的信号/插槽通信的开销可能甚至比从数据库加载单个行的开销还要高。

I would recommend to collect the rows in a QList instance the while loop. 我建议在while循环中收集QList实例中的行。 When done, push the complete result via one signal invocation to the main thread: 完成后,通过一次信号调用将完整结果推送到主线程:

First declare a simple class for storing history items: 首先声明一个用于存储历史记录项目的简单类:

class HistoryItem {
    public:
        QString channelID;
        /* additonal fields omitted for brevity */
        /* also, private fields with getters and setters would be better */
}

Then, create a list of such objects before the while loop: 然后,在while循环之前创建此类对象的列表:

QList<HistoryItem*> innerResult;
while (queryInner.next()) {
      /* snip */
      HistoryItem* item = new HistoryItem();
      item.channelId = channelIDInner;
      /* more lines ommited */
      innerResult.append(historyItem);
}
emit historyLoaded(innerResult);

Obviously, you also need a matching signal definition in your worker class: 显然,您还需要在worker类中使用匹配的信号定义:

Q_SIGNAL void historyLoaded(QList<HistoryItem*> result);    

Also as noted in the comments, you have to start the background thread with QThread::start() . 同样如注释中所述,您必须使用QThread::start()启动后台线程。

You may benefit from continuous loading: 您可以从连续加载中受益:

You only load the elements that will be in view and only request the next set when they get scrolled into view (or just at a much lower pace in the background in general). 您只加载将在视图中显示的元素,并且仅在它们滚动到视图中时才请求下一组(或通常在背景中以较低的速度移动)。

This can for example be done with Adding an object to the window object with the appropriate signal and letting the js trigger it when the end becomes visible. 例如,可以通过使用适当的信号将一个对象添加到window对象并让js在结束可见时触发它来完成此操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM