如果你想在小部件显示时执行某些操作,可以像这样覆盖QWidget::showEvent:
class YourWidget : public QWidget { ...
void YourWidget::showEvent( QShowEvent* event ) {
QWidget::showEvent( event );
//your code here
}
在分析了以上解决方案后,发现它们全部都有问题,包括那些得票最高的解决方案。
许多人推荐像这样的方法:
class MyWidget : public QWidget {
// ...
};
void MyWidget::showEvent(QShowEvent* event) {
QWidget::showEvent(event);
DoSomething();
}
void MyWidget::DoSomething() {
// ...
}
只要在DoSomething
中没有QCoreApplication::processEvents();
,这个方法就能正常工作。 如果有一个这样的调用,它会处理队列中的所有事件,包括最初调用MyWidget::showEvent的QShowEvent
。 当它到达原始的QShowEvent时,它会再次调用MyWidget::showEvent,导致无限循环。
如果出现这种情况,有三种解决方案:
解决方案1:避免在MyWidget::DoSomething
中调用processEvents
,而是在必要时调用update
或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。
试试这个:
在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
}
connect(this, SIGNAL(window_loaded()), this, SLOT(your_function()));
。 - vsz按照Reza Ebrahimi的做法,但要记住:
不要省略connect()
函数的第五个参数,该参数指定连接类型,确保它为QueuedConnection
。
即,
connect(this, SIGNAL(window_loaded), this, SLOT(your_function()), Qt::ConnectionType(Qt::QueuedConnection | Qt::UniqueConnection));
如果您按照这种方式做,我相信您可以实现所需的目标。
AutoConnection
,DirectConnection
,QueuedConnection
,BlockingQueuedConnection
(+ 可选的 UniqueConnection
)。详细信息请阅读手册。 :)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执行某些操作,但这似乎很不可能。这是需要牢记并进行代码防御的事项。
QMetaObject::invokeMethod(this, &MainWindow::afterWindowShown, Qt::ConnectionType::QueuedConnection);
这样你就不用使用字符串函数名了。 - Youda008QTimer::singleShot( 0, ... );
方法有何不同。 - Youda008//- 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
我使用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;
}
QApplication::processEvents()
以避免UI冻结)。
showEvent
、排队信号和立即计时器(0毫秒)都不好:我怀疑它们在事件堆栈中太早了,最多只能得到屏幕上的垃圾。显然,任何基于任意计时器(例如50毫秒)的解决方案都高度依赖目标系统的速度,这意味着它不可靠。 - syampublic:
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++;
}
}
void show()
,如下所示:void MainWindow::show()
{
QMainWindow::show();
// Call your special function here.
}
QMainWindow::show()
,然后调用QMetaObject::invokeMethod()
并执行任何其他你想要做的操作。 - Trilarion