Используя Qt, я создаю QMainWindow и хочу вызвать функцию ПОСЛЕ того, как будут показаны окна. Когда я вызываю функцию в конструкторе , функция (диалог фактически) вызывается до того, как будет показано окно.
Как вызывать функцию после окна?
Ответ 1
Если вы хотите что-то сделать, пока виджет станет видимым, вы можете переопределить QWidget:: showEvent следующим образом:
class YourWidget : public QWidget { ...
void YourWidget::showEvent( QShowEvent* event ) {
QWidget::showEvent( event );
//your code here
}
Ответ 2
попробуй это:
в 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
}
Ответ 3
Следуйте примеру Резы Эбрахими, но помните об этом:
Не опускайте 5-й параметр функции connect()
, который указывает тип подключения; убедитесь, что оно QueuedConnection
.
I.E.,
connect(this, SIGNAL(window_loaded), this, SLOT(your_function()), Qt::ConnectionType(Qt::QueuedConnection | Qt::UniqueConnection));
Я считаю, что вы достигнете того, что вам нужно, если вы сделаете это так.
- В соединениях с сигнальным слотом существует несколько типов:
AutoConnection
,DirectConnection
,QueuedConnection
,BlockingQueuedConnection
(+ optionalUniqueConnection
). Подробнее читайте в руководстве.:)
Ответ 4
Я нашел хороший ответ в этом вопросе, который хорошо работает, даже если вы используете функцию 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
Ответ 5
Я решил это без таймера, используя событие 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;
}
Ответ 6
Лучшее решение для меня - подсчет события красок:
.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++;
}
}
Ответ 7
Метод переопределения void show()
следующим образом:
void MainWindow::show()
{
QMainWindow::show();
// Call your special function here.
}
Ответ 8
Предполагая, что вы хотите запустить свой код в потоке пользовательского интерфейса окна после того, как окно было показано, вы можете использовать следующий относительно компактный код.
class MainWindow : public QMainWindow
{
// constructors etc omitted.
protected:
void showEvent(QShowEvent *ev)
{
QMainWindow::showEvent(ev);
// Call slot via queued connection so it 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, но это кажется маловероятным. Что-то, что нужно иметь в виду и защищать, возможно, против.
Ответ 9
После анализа приведенных выше решений выясняется, что большинство из них, в том числе и те, которые были подвергнуты сильному голосованию, неисправны.
Многие рекомендуют что-то вроде этого:
class MyWidget : public QWidget {
// ...
};
void MyWidget::showEvent(QShowEvent* event) {
QWidget::showEvent(event);
DoSomething();
}
void MyWidget::DoSomething() {
// ...
}
Это работает до тех пор, пока нет QCoreApplication::processEvents();
в DoSomething
. Если он есть, он обрабатывает все события в очереди, включая QShowEvent
который вызвал MyWidget :: showEvent. Когда он попадает в исходный QShowEvent, он снова вызывает MyWidget :: showEvent, вызывая бесконечный цикл.
Если это произойдет, есть три решения:
Решение 1. Избегайте вызова processEvents
в MyWidget::DoSomething
, вместо этого вызывайте 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 в конструкторе.