42

使用 Qt 我创建了一个QMainWindow并希望在显示窗口后调用一个函数。当我在构造函数中调用该函数时,该函数(实际上是一个对话框)在显示窗口之前被调用。

4

9 回答 9

38

如果你想在小部件可见时做一些事情,你可以像这样覆盖QWidget::showEvent

class YourWidget : public QWidget { ...

void YourWidget::showEvent( QShowEvent* event ) {
    QWidget::showEvent( event );
    //your code here
} 
于 2013-01-16T11:50:04.120 回答
15

尝试这个:

在 mainwindow.h 中:

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();  

protected:
      void showEvent(QShowEvent *ev);

private:
      void showEventHelper();
      Ui::MainWindow *ui;
}

在 mainwindow.cpp 中:

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

void MainWindow::showEvent(QShowEvent *ev)
{
    QMainWindow::showEvent(ev);
    showEventHelper();
}

void MainWindow::showEventHelper()
{
    // your code placed here
}
于 2013-01-23T16:55:41.880 回答
13

遵循 Reza Ebrahimi 的示例,但请记住这一点:

不要省略connect()指定连接类型的函数的第 5 个参数;确保它是QueuedConnection

IE,

connect(this, SIGNAL(window_loaded), this, SLOT(your_function()), Qt::ConnectionType(Qt::QueuedConnection | Qt::UniqueConnection));

我相信如果你这样做,你会达到你所需要的。

  • 信号槽连接有几种类型:AutoConnection, DirectConnection, QueuedConnection, BlockingQueuedConnection(+ optional UniqueConnection)。阅读手册了解详情。:)
于 2014-10-30T10:01:55.820 回答
13

在分析了上面的解决方案之后,所有这些解决方案,包括那些被大力支持的解决方案,都是错误的。

许多人推荐这样的东西:

class MyWidget : public QWidget {
    // ...
};

void MyWidget::showEvent(QShowEvent* event) {
    QWidget::showEvent(event);
    DoSomething();
}

void MyWidget::DoSomething() {
    // ...
}

只要没有QCoreApplication::processEvents();in就可以使用DoSomething。如果有,它会处理队列中的所有事件,包括QShowEvent首先调用 MyWidget::showEvent 的事件。当它到达原始的 QShowEvent 时,它再次调用 MyWidget::showEvent,导致无限循环。

如果发生这种情况,有以下三种解决方案:

解决方案 1. 避免调用processEventsin MyWidget::DoSomething,而是在必要时调用updateor 。repaint如果DoSomething调用别的东西,这些函数processEvents也应该避免。

解决方案 2. 使 DoSomething 成为一个插槽,并将对 DoSomething() 的直接调用替换为

QTimer::singleShot(0, this, SLOT(DoSomething()));

由于零间隔计时器仅在队列中的所有事件都处理完毕后才会触发,因此它将处理所有事件,包括原始 QShowEvent,将它们从队列中移除,然后才调用 DoSomething。我最喜欢它。

由于只有在处理完队列中的所有事件后才会触发零间隔计时器,因此您不应尝试通过延长间隔来“改进”它,例如

QTimer::singleShot(50, this, SLOT(DoSomething())); // WRONG!

由于 50 毫秒通常足以处理队列中的事件,这通常会起作用,从而导致难以重现的错误。

解决方案 3. 制作一个防止第二次调用 DoSomething 的标志:

class MyWidget : public QWidget {
    // ...
};

void MyWidget::showEvent(QShowEvent* event) {
    if (is_opening)
        return;
    is_opening = true;
    QWidget::showEvent(event);
    DoSomething();
    is_opening = false;
}

void MyWidget::DoSomething() {
    // ...
}

这里,is_opening 是一个布尔标志,应该在构造函数中初始化为 false。

于 2019-07-02T12:46:55.127 回答
4

假设您想在窗口显示在窗口的 UI 线程中运行代码,您可以使用以下相对紧凑的代码。

class MainWindow : public QMainWindow
{
        // constructors etc omitted.

protected:
    void showEvent(QShowEvent *ev)
    {
        QMainWindow::showEvent(ev);
        // Call slot via queued connection so it's called from the UI thread after this method has returned and the window has been shown
        QMetaObject::invokeMethod(this, "afterWindowShown", Qt::ConnectionType::QueuedConnection);
    }

private slots:
    void afterWindowShown()
    {
        // your code here
        // note this code will also be called every time the window is restored from a minimized state
    }
};

它确实按名称调用 afterWindowShown,但这种事情在 Qt 中是相当普遍的做法。有一些方法可以避免这种情况,但它们有点冗长。

请注意,此代码应适用于任何 QWidget 派生类,而不仅仅是 QMainWindow 派生类。

理论上,非常快速的用户可能会在调用 afterWindowShown 之前在显示窗口的 UI 上调用某种操作,但这似乎不太可能。一些需要牢记的东西并可能防御性地编写代码。

于 2018-10-11T16:52:58.293 回答
2

我在这个问题中找到了一个很好的答案,即使您使用 Sleep() 函数,它也很有效。

所以尝试了这个:

//- cpp-file ----------------------------------------

#include "myapp.h"
#include <time.h>
#include <iosteream>

MyApp::MyApp(QWidget *parent)
    : QMainWindow(parent, Qt::FramelessWindowHint)
{
    ui.setupUi(this);
}

MyApp::~MyApp()
{

}

void MyApp::showEvent(QShowEvent *event) {
    QMainWindow::showEvent(event);
    QTimer::singleShot(50, this, SLOT(window_shown()));
    return;
}

void MyApp::window_shown() {
    std::cout << "Running" << std::endl;
    Sleep(10000);
    std::cout << "Delayed" << std::endl;
    return;
}

//- h-file ----------------------------------------

#ifndef MYAPP_H
#define MYAPP_H

#include <QtWidgets/QMainWindow>
#include <qtimer.h>
#include <time.h>
#include "ui_myapp.h"


class MyApp : public QMainWindow
{
    Q_OBJECT

public:
    MyApp(QWidget *parent = 0);
    ~MyApp();

protected:
    void showEvent(QShowEvent *event);


private slots:
    void window_shown();

private:
    Ui::MyAppClass ui;
};

#endif // MYAPP_H
于 2016-08-03T17:14:51.883 回答
1

我使用 Paint 事件在没有计时器的情况下解决了它。至少在 Windows 上对我有用。

// MainWindow.h
class MainWindow : public QMainWindow
{
    ...
    bool event(QEvent *event) override;
    void functionAfterShown();
    ...
    bool functionAfterShownCalled = false;
    ...
}

// MainWindow.cpp
bool MainWindow::event(QEvent *event)
{
    const bool ret_val = QMainWindow::event(event);
    if(!functionAfterShownCalled && event->type() == QEvent::Paint)
    {
        functionAfterShown();
        functionAfterShownCalled = true;
    }
    return ret_val;
}
于 2016-09-27T18:28:20.160 回答
1

对我来说最好的解决方案是计算一次绘制事件:

。H

public:
void paintEvent(QPaintEvent *event);

.CPP

#include "qpainter.h"
#include <QMessageBox> // example

int contPaintEvent= 0;

void Form2::paintEvent(QPaintEvent* event)
{
  if (contPaintEvent ==0 )
  {
  QPainter painter(this); 
  QMessageBox::information(this, "title", "1 event paint"); // example
  // actions
  contPaintEvent++;
  }
}
于 2017-05-03T11:53:24.623 回答
0

像这样重新实现方法void show()

void MainWindow::show()
{
    QMainWindow::show();
    // Call your special function here.
}
于 2016-10-24T07:49:31.147 回答