C++ QT-主窗口不';除非它';关门了

C++ QT-主窗口不';除非它';关门了,c++,qt,user-interface,C++,Qt,User Interface,我试图通过每500毫秒在线程中调用updateGUI函数来更新主窗口。除非关闭窗口,否则将显示该窗口,但不会使用新值进行更新。执行此操作时,将使用新值打开一个新窗口。我找到了,但它没有回答我的问题。我知道(如qt文档中所述) QApplication::exec进入主事件循环并等待 调用exit() 我尝试使用processEvents(),但主窗口反复打开和关闭,速度非常快,我甚至看不到它。这是我的密码: float distanceToObject; bool objectDetected;

我试图通过每500毫秒在线程中调用
updateGUI
函数来更新主窗口。除非关闭窗口,否则将显示该窗口,但不会使用新值进行更新。执行此操作时,将使用新值打开一个新窗口。我找到了,但它没有回答我的问题。我知道(如qt文档中所述)

QApplication::exec
进入主事件循环并等待 调用
exit()

我尝试使用
processEvents()
,但主窗口反复打开和关闭,速度非常快,我甚至看不到它。这是我的密码:

float distanceToObject;
bool objectDetected;
Modes currentMode;

void timerStart(std::function<void(void)> func, unsigned int interval)
{
    std::thread([func, interval]()
    {
        while (true)
        {
            auto x = std::chrono::steady_clock::now() + std::chrono::milliseconds(interval);
            func();
            std::this_thread::sleep_until(x);
        }
    }).detach();
}

int updateGUI(void)
{
    int argc = 0;
    char **argv = NULL;

    QApplication a(argc, argv);
    MainWindow w;
    // Set text of a label
    w.setDistance(QString::number(distanceToObject));
    // Also update objectDetected and currentMode values
    w.show();
    //a.processEvents();
    return a.exec();
}

void sendMsg(void)
{
    // Send heartbeat signal to another device
}

void receiveMsg(void)
{
    // Read messages from the other device and update the variables
    // These two values change continuously
    objectDetected = true;
    distanceToObject = 5.4;
}

void decide(void)   
{
    // The core function of the program. Takes relatively long time
    // Run a decision-making algorithm which makes decisions based on the values received from the other device. 
    // Update some variables according to the made decisions
    currentMode = Auto;
    // Execute functions according to the made decisions. 
    setMode(currentMode);
}

int main(void)
{
    timerStart(updateGUI, 500);
    timerStart(sendMsg, 1000);
    timerStart(receiveMsg, 10); 
    timerStart(decide, 500);
}
浮动距离对象;
检测到布尔对象;
模式电流模式;
void timerStart(std::function func,unsigned int interval)
{
线程([func,interval]()
{
while(true)
{
自动x=std::chrono::Standy_clock::now()+std::chrono::毫秒(间隔);
func();
std::this_thread::sleep_until(x);
}
}).detach();
}
int updateGUI(void)
{
int argc=0;
字符**argv=NULL;
质量保证申请a(argc、argv);
主窗口w;
//设置标签的文本
w、 setDistance(QString::number(distanceToObject));
//同时更新objectDetected和currentMode值
w、 show();
//a、 processEvents();
返回a.exec();
}
void sendMsg(void)
{
//向其他设备发送心跳信号
}
void receiveMsg(void)
{
//从其他设备读取消息并更新变量
//这两个值不断变化
objectDetected=true;
距离对象=5.4;
}
作废决定(作废)
{
//程序的核心功能。需要相对较长的时间
//运行决策算法,该算法根据从其他设备接收到的值做出决策。
//根据做出的决策更新一些变量
电流模式=自动;
//根据做出的决策执行功能。
设置模式(当前模式);
}
内部主(空)
{
timerStart(updateGUI,500);
timerStart(sendMsg,1000);
timerStart(接收消息,10);
timerStart(决定,500);
}

如何使用变量的值正确更新主窗口?

您的线程不会更新
MainWindow
,但会在每次迭代中创建一个全新的
QApplication
MainWindow
。在退出应用程序(例如关闭窗口)之前,您的线程应该卡在
QApplication::exec
内。只有这样,线程的循环才能取得进一步的进展

通常,从主线程外部执行更新时必须非常小心,因为通常GUI操作必须在主线程内部执行

考虑使用
QThread
,它已经附带了自己的事件循环,您可以使用它来通知/更新使用相应插槽的窗口

如果没有关于你真正想要达到的目标的更多细节,就不可能给你更多的方向。一、 至少,建议您在主线程内创建
QApplication
main窗口
(例如
main
)。然后,这取决于您试图“更新”的内容。如果您需要处理一些数据,那么您可以在第二个线程中执行该操作,并使用信号槽将结果发送到
MainWindow
实例。如果需要在窗口上绘制,则必须直接在主线程中完成,或者可以找到一种方法从线程中渲染到单独的缓冲区(即
QImage
),然后将此缓冲区发送到主线程,以便将其绘制到窗口中


我试图勾勒出这样的事情是如何做到的。然而,请注意,它既不完整也不可编译,只是一个大纲

首先,您有一个
main窗口
,并在其中添加一个
信号
,通知所有观察者开始工作(稍后将变得清晰)。此外,您还添加了
插槽
,每当您的某个值发生更改时,将调用这些插槽。这些
插槽
在主线程中运行(并且是
主窗口
的成员),因此可以根据需要更新窗口:

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    // constructors and stuff

    void startWorking()
    {
        emit startWorkers();   
    }

public slots:
    void onModeChanged(Modes m)
    {
        // update your window with new mode
    }

    void onDistanceChanged(float distance)
    {
        // update your window with new distance
    }

signals:
    void startWorkers();
};
接下来,构建一个
Worker
类,该类封装了您喜欢做的所有“后台工作”(基本上是您的线程在原始代码中所做的):

请注意,
Worker
必须继承
QObject
,并且您的
doWork
方法必须是
公共插槽。此外,您还可以为要通知的
主窗口
的每个值添加一个
信号
。它们不需要实现,因为它是由Qt-MOC(元对象编译器)生成的。每当相应值中的一个发生变化时,只需发出相应的
信号
并传递新值即可

最后,你把所有的东西放在一起:

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);
    MainWindow window;

    // create a worker object
    Worker* worker = new Worker;

    // connect signals and slots between worker and main window
    QObject::connect(worker, &Worker::modeModified, 
        &window, &MainWindow::onModeChanged);
    QObject::connect(worker, &Worker::distanceModified, 
        &window, &MainWindow::onDistanceChanged);
    QObject::connect(&window, &MainWindow::startWorkers, 
        worker, &Worker::doWork);

    // create a new thread
    QThread* thread = new QThread;
    // send worker to work inside this new thread
    worker->moveToThread(thread);
    thread->start();

    // show window and start doing work    
    window.show();
    window.startWorking();

    // start main loop
    int result = app.exec();    

    // join worker thread and perform cleanup
    return result;
}
好吧,让我们来看看。首先,在主线程内创建
QApplication
MainWindow
。接下来,创建
Worker
对象的一个实例(可以在这里创建多个)。然后将
工作人员的信号连接到
窗口的插槽,反之亦然。一旦建立了这些连接,每当发出信号时,Qt就会调用连接的插槽(并传输传递的值)。请注意,此连接跨线程边界工作。每当从与接收对象的线程不同的线程发出信号时,Qt将发送一条消息,该消息在接收对象的线程中处理

然后,使用
QObject::moveToThread
告诉Qt您希望您的
工作线程生活在另一个线程中。有关如何正确使用
QThread
及其内部对象的详细说明,请参阅

其余的是
int main(int argc, char* argv[])
{
    QApplication app(argc, argv);
    MainWindow window;

    // create a worker object
    Worker* worker = new Worker;

    // connect signals and slots between worker and main window
    QObject::connect(worker, &Worker::modeModified, 
        &window, &MainWindow::onModeChanged);
    QObject::connect(worker, &Worker::distanceModified, 
        &window, &MainWindow::onDistanceChanged);
    QObject::connect(&window, &MainWindow::startWorkers, 
        worker, &Worker::doWork);

    // create a new thread
    QThread* thread = new QThread;
    // send worker to work inside this new thread
    worker->moveToThread(thread);
    thread->start();

    // show window and start doing work    
    window.show();
    window.startWorking();

    // start main loop
    int result = app.exec();    

    // join worker thread and perform cleanup
    return result;
}
class Worker : public QObject
{
    Q_OBJECT
public:
    Worker()
    {
        QObject::connect(&modeTimer_, &QTimer::timeout,
            this, &Worker::onModeTimerTimeout);
        QObject::connect(&distanceTimer_, &QTimer::timeout,
            this, &Worker::onDistanceTimerTimeout);

        modeTimer_.start(500); // emit timeout() every 500ms
        distanceTimer_.start(100); // emit timeout() every 100ms
    }

public slots:    
    void onModeTimerTimeout()
    {
        // recompute mode
        Modes m = // ...
        emit modeModified(m);
    }

    void onDistanceTimerTimeout()
    {
        // recompute distance
        float distance = // ...
        emit distanceModified(distance);
    }

signals:
    void modeModified(Modes m);
    void distanceModified(float distance);

private:
    QTimer modeTimer_;
    QTimer distanceTimer_;
};