C++ 显示窗口后如何调用函数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14356121/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to call function after window is shown?
提问by HWende
Using Qt I create a QMainWindowand want to call a function AFTER the windows is shown. When I call the function in the constructorthe function (a dialog actually) get's called before the window is shown.
使用 Qt 我创建了一个QMainWindow并想在显示窗口后调用一个函数。当我在构造函数中调用该函数时,该函数(实际上是一个对话框)在窗口显示之前被调用。
回答by Frank Osterfeld
If you want to do something while the widget is made visible, you can override QWidget::showEventlike this:
如果你想在小部件可见时做一些事情,你可以像这样覆盖QWidget::showEvent:
class YourWidget : public QWidget { ...
void YourWidget::showEvent( QShowEvent* event ) {
QWidget::showEvent( event );
//your code here
}
回答by Reza Ebrahimi
try this:
尝试这个:
in mainwindow.h:
在 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;
}
in mainwindow.cpp:
在 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
}
回答by Kieung Kim
Follow Reza Ebrahimi's example, but keep this in mind:
遵循 Reza Ebrahimi 的示例,但请记住这一点:
Do not omit the 5th parameter of connect()
function which specifies the connection type; make sure it to be QueuedConnection
.
不要省略connect()
指定连接类型的函数的第 5 个参数;确保它是QueuedConnection
.
I.E.,
IE,
connect(this, SIGNAL(window_loaded), this, SLOT(your_function()), Qt::ConnectionType(Qt::QueuedConnection | Qt::UniqueConnection));
I believe that you'd achieve what you need if you do it this way.
我相信如果你这样做,你会达到你所需要的。
- There are several types in signal-slot connections:
AutoConnection
,DirectConnection
,QueuedConnection
,BlockingQueuedConnection
(+ optionalUniqueConnection
). Read the manual for details. :)
- 信号槽连接有多种类型:
AutoConnection
、DirectConnection
、QueuedConnection
、BlockingQueuedConnection
(+ 可选UniqueConnection
)。阅读手册了解详细信息。:)
回答by user31264
After analyzing the solutions above, it turns that all of them, including the heavily upvoted ones, are faulty.
在分析了上述解决方案后,发现所有解决方案,包括高票数的解决方案,都是错误的。
Many recommend something like this:
许多人推荐这样的东西:
class MyWidget : public QWidget {
// ...
};
void MyWidget::showEvent(QShowEvent* event) {
QWidget::showEvent(event);
DoSomething();
}
void MyWidget::DoSomething() {
// ...
}
This works as long as there is no QCoreApplication::processEvents();
in DoSomething
. If there is one, it processes all events in the queue, including the QShowEvent
which called MyWidget::showEvent in the first place. When it gets to the original QShowEvent, it calls MyWidget::showEvent again, causing an infinite loop.
只要没有QCoreApplication::processEvents();
in 就可以工作DoSomething
。如果有,它会处理队列中的所有事件,包括QShowEvent
首先调用 MyWidget::showEvent 的事件。当它到达原始 QShowEvent 时,它再次调用 MyWidget::showEvent,导致无限循环。
If this happens, there are three solutions:
如果发生这种情况,有以下三种解决方案:
Solution 1. Avoid calling processEvents
in MyWidget::DoSomething
, instead call update
or repaint
when necessary. If DoSomething
calls something else, these functions should avoid processEvents
also.
解决方法1.避免调用processEvents
的MyWidget::DoSomething
,而不是打电话update
或repaint
在必要时。如果DoSomething
调用别的东西,这些函数processEvents
也应该避免。
Solution 2. Make DoSomething a slot, and replace direct call to DoSomething() by
解决方案 2. 将 DoSomething 设为插槽,并将对 DoSomething() 的直接调用替换为
QTimer::singleShot(0, this, SLOT(DoSomething()));
Since zero interval timer fires only when after all events in the queue are processed, it will process all events, including the original QShowEvent, remove them from the queue, and only then call DoSomething. I like it the most.
由于零间隔计时器仅在队列中的所有事件都处理完后才触发,因此它将处理所有事件,包括原始 QShowEvent,将它们从队列中删除,然后才调用 DoSomething。我最喜欢它。
Since only zero interval timer fires only when after all events in the queue are processed, you should not try to "improve" it by lengthening the interval, for instance
由于只有在处理完队列中的所有事件后才会触发零间隔计时器,因此您不应该尝试通过延长间隔来“改进”它,例如
QTimer::singleShot(50, this, SLOT(DoSomething())); // WRONG!
Since 50 ms is usuallyenough time for processing events in the queue, that would usuallywork, causing an error which is hard to reproduce.
由于 50 毫秒通常足以处理队列中的事件,这通常会起作用,从而导致难以重现的错误。
Solution 3. Make a flag which prevents calling DoSomething the second time:
解决方案 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() {
// ...
}
Here, is_opening is a boolean flag which should be initialized as false in constructor.
这里,is_opening 是一个布尔标志,它应该在构造函数中初始化为 false。
回答by hexadecimal
The best solution for me is count once paint event:
对我来说最好的解决方案是计数一次绘制事件:
.H
。H
public:
void paintEvent(QPaintEvent *event);
.CPP
.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++;
}
}
回答by persiflage
Assuming you want to run your code in the UI thread of the window afterthe window has been shown you could use the following relatively compact code.
假设您想在窗口显示后在窗口的 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
}
};
It does invoke afterWindowShown by name but that sort of thing is fairly common practice in Qt. There are ways of avoiding this but they're a bit more verbose.
它确实按名称调用 afterWindowShown ,但这种事情在 Qt 中是相当普遍的做法。有一些方法可以避免这种情况,但它们有点冗长。
Note that this code should work for any QWidget derived class, not just QMainWindow derived classes.
请注意,此代码应该适用于任何 QWidget 派生类,而不仅仅是 QMainWindow 派生类。
In theory it might be possible for a very quick user to invoke some sort of action on the UI of the displayed window before afterWindowShown can be called but it seems unlikely. Something to bear in mind and code defensively against perhaps.
从理论上讲,在调用 afterWindowShown 之前,非常快速的用户可能会在显示窗口的 UI 上调用某种操作,但似乎不太可能。一些需要牢记的东西,也许可以防御性地编码。
回答by Michael Sasser
I found a nice answer in this questionwhich works well, even if you use a Sleep() function.
我在这个问题中找到了一个很好的答案,即使您使用 Sleep() 函数,它也能很好地工作。
So tried this:
所以试过这个:
//- 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
回答by RuslanK
I solved it without a timer using Paint event. Works for me at least on Windows.
我使用 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;
}
回答by klasyc
Reimplement method void show()
like this:
void show()
像这样重新实现方法:
void MainWindow::show()
{
QMainWindow::show();
// Call your special function here.
}