Подтвердить что ты не робот

Как вызывать функцию после окна?

Используя Qt, я создаю QMainWindow и хочу вызвать функцию ПОСЛЕ того, как будут показаны окна. Когда я вызываю функцию в конструкторе , функция (диалог фактически) вызывается до того, как будет показано окно.

4b9b3361

Ответ 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 (+ optional UniqueConnection). Подробнее читайте в руководстве.:)

Ответ 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 в конструкторе.