2016-10-06 5 views
2

У меня возникают проблемы с пониманием, как использовать QTimer, так что я бы повторить определенное действие (нажатия кнопки или сетевого запроса).Как использовать QTimer повторить определенное действие

После answer из этого славного question, я не могу понять, как подключить QTimer::Timeout сигнал таймера на MainWIndow::request сигнала точно так же, как у меня есть кнопка подключенного в данный момент. Проблема в том, что я не могу повторить это; он компилируется и запускается без ошибок, но без повтора.

(Как человек, который отвечал на вопрос, я тоже могу иметь повторение таймера, если я ставлю все мой код в main.cpp, но я хотел бы увидеть, как именно это можно сделать в этом случае.)

Ниже приведен упрощенный код взят из этого примера, чтобы указать мои попытки:

#include "mainwindow.h" 
#include "ui_mainwindow.h" 
#include <QKeyEvent> 
#include <QApplication> 
#include <QtWidgets> 
#include <QTimer> 

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

    // GUI setup here // 

    networkManager = new QNetworkAccessManager; 

    connect(networkManager, &QNetworkAccessManager::finished, this, &MainWindow::on_NetworkManagerFinished); 
    connect(ui->getButton, &QPushButton::clicked, this, &MainWindow::on_getButton_clicked); 

    // Connect the timer to repeat the GET request 
    QTimer timer; 

    // [1st attempt] 
    connect(&timer, SIGNAL(&QTimer::timeout), this, SLOT(&MainWindow::on_TimerTimeout)); 

    // [2nd attempt] 
    connect(&timer, SIGNAL(timeout()), this, SLOT(timer_buttonClicked())); 

    timer.start(1500); // 1.5 secs 
} 

MainWindow::~MainWindow() 
{ 
    delete ui; 
} 

void MainWindow::on_NetworkManagerFinished(QNetworkReply *reply) 
{ 
     // Parse and display JSON here // 
} 

// Make GET request when button is clicked 
void MainWindow::on_getButton_clicked() //on_TimerTimeout() 
{ 
    // Make GET request  
    QUrlQuery query; 

    QUrl url("http://blah/blahblah"); 
    query.addQueryItem("blah", "blah"); 
    url.setQuery(query); 
    QNetworkRequest networkRequest(url); 

    networkManager->get(networkRequest); 

    ui->getButton->setEnabled(false); 

    // Restart timer 
    timer->start(1500); // 1.5 secs 

    // Do stuff in the GUI here // 
} 

обновление

class MainWindow : public QMainWindow 
{ 
    Q_OBJECT 

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

private slots: 
    void on_NetworkManagerFinished(QNetworkReply* reply); 
    void on_getButton_clicked(); 

private: 
    Ui::MainWindow *ui; 
    QNetworkAccessManager *networkManager; 
}; 

#endif // MAINWINDOW_H 

Как можно видеть в разделе комментариев, я также попытался в заменить частный слот для кнопки с onTimeout и в mainwindow.cpp перед подключением я создал объект QTimer и запустил его, и сразу после этого я использовал этот объект для определения connect(&timer,&QTimer::timeout,this,&MainWindow::onTimeout)‌​;. Кроме того, я заменил определение слота кнопки в конце кодом, связанным с onTimeout.

Если вы можете объяснить, что мне не хватает, или даже предоставить код для точного примера, как показано here, что было бы замечательно для меня.

+1

Вы создаете таймер как локальный в конструкторе, то есть таймер уничтожается после возвращения конструктора. Либо распределите его динамически 'QTimer * timer = new QTimer' или лучше - сделайте таймер членом класса. – dtech

+0

Я очень сомневаюсь, что вы хотите, чтобы таймер нажимал кнопки или что-то в этом роде. Вы хотите, чтобы таймер вызывал все действия, связанные с нажатием кнопки. Это совсем другое дело. –

ответ

2

Фрагмент кода ниже иллюстрирует правильный подход на основе кода вы предоставили:

#include <QTimer>  

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { 

// setup your GUI 

// declare a pointer variable for pointing to a QTimer object 
QTimer *timer = new QTimer; 

// make the connection using the timer variable 
connect(timer, &QTimer::timeout, this, &MainWindow::on_getButton_clicked); 

// start the timer object by first dereferencing that object first 
timer->start(1500); 

// do other stuff // 

} 

Вы должны сделать некоторые дополнительные чтения на указателей и differences с точки оператора (.).

Примечание

ddriver «s ответ лучше с помощью таймера в качестве класса члена; это позволяет избежать локальности ссылки указателя, как в моем подходе.

+1

Подход ddriver лучше с использованием таймера в качестве члена ** ** **, но, возможно, немного продвинутый для вас с учетом вашего уровня (без обид). Было бы хорошо, если бы вы могли понять, почему подход членов класса является оптимальным; последующее домашнее задание :) – Yannis

+0

То, что я хотел, спасибо за то, что написал его для меня, даже если ddriver предложил это в своих комментариях. Теперь я попытаюсь выяснить подход класса, спасибо –

+0

Важно отметить, что этот код приведет к потере вашего таймера после возврата конструктора. Это означает, что вы не сможете остановить его или каким-либо образом изменить его. Единственной ссылкой на таймер является его указатель, который является локальным конструктором, поэтому рекомендуется иметь либо указатель, либо сам таймер в качестве члена класса. – dtech

2

Если ваш код даже компилируется, это означает, что очень вероятно, что у вас есть член QTimer *timer в вашем классе. Вы можете вывести это из этих линий:

// Restart timer 
timer->start(1500); // 1.5 secs 

Если вы не имеете его, а затем добавить его к классу:

class MainWindow { 
    Q_OBJECT 
... 
private: 
    QTimer *timer; // add this 
}; 

Однако в вашем конструкторе класса, вы создаете другой переменная называется timer, слежка элемент данных:

// Connect the timer to repeat the GET request 
QTimer timer; 

Вы определенно не хотите, чтобы ввести новую переменную там. Более того, эта переменная имеет автоматическое хранилище, что означает, что оно будет уничтожено в конце его охватывающей области (что является самим конструктором: поэтому, когда вы возвращаетесь из конструктора, объект timer отсутствует).

Решение: просто не объявляйте новую переменную, используйте существующий элемент данных. Измените конструктор, чтобы что-то вроде:

// not introducing any new variable here! 
timer = new QTimer(this); 
connect(timer, ...); 

О фактических connect заявления.

Это один просто неправильно:

connect(&timer, SIGNAL(&timeout()), this, SLOT(&MainWindow::on_TimerTimeout)); 

Он будет собирать, но испускают предупреждения во время выполнения. Избавиться от этого. (Как же вы в конечном итоге, даже набрав этот Если вы используете Qt Creator вы бы автодополнение для сигналов и слотов макросов ...?)

Это один:

connect(&timer, SIGNAL(timeout()), this, SLOT(timer_buttonClicked())); 

имеет правильный синтаксис, но я не могу сказать, будет ли это действительно работать, есть ли функция участника void timer_buttonClicked(), помеченная как слот? Другой connect использует другую функцию.

И если вы используете Qt 5, двигаться как можно быстрее, чтобы:

connect(timer, &QTimer::timeout, this, &MainWindow::whatever); 

которая выйдет из строя во время компиляции, если что-то не так.

+0

Я пробовал то, что вы предложили, но я получаю ошибки; ** если я делаю ** 'таймер QTimer; timer = новый QTimer (это); connect (timer, & QTimer :: timeout, this, & MainWindow :: on_getButton_clicked); 'I get 'C2679 не найден оператор, который принимает правый операнд типа QTimer'. ** если я делаю ** 'timer = new QTimer (this); connect (таймер, & QTimer :: timeout, this, & MainWindow :: on_getButton_clicked); 'Я получаю таймер незаявленного идентификатора '. * Я обновил свое описание, чтобы показать свои слоты * – Karim

+0

@ Karim - всегда полезно знать язык, прежде чем пытаться использовать его на практике. – dtech

+0

@ddriver Я понимаю, что я могу задавать глупые вопросы, но здесь я прошу, читая и ищу руководство, чтобы «выучить язык», как вы говорите. Извините, если это вас раздражает. – Karim

2

Я бы сказал, что самое простое решение было бы просто сделать таймер членом класса, там действительно нет необходимости использовать выделение кучи:

class YourClass : public QObject { 
    Q_OBJECT 
    QTimer timer; 
    public: 
    YourClass() { 
    connect(&timer, SIGNAL(timeout()), this, SLOT(timer_yourSlot())); 
    // or the new and safer syntax in Qt 5 
    // connect(&timer, &QTimer::timeout, this, &YourClass::yourSlot); 
    timer.start(1500); 
    } 
    public slots: 
    void yourSlot() { ... } 
}; 

Рекомендуется, чтобы избежать выделения кучи, если вы не знаете, что это что вам нужно. И в этом случае вам это не нужно. Причин избежать этого много - он медленнее, он будет использовать больше памяти, он может выйти из строя (если у вас закончится нехватка памяти), для этого потребуется дополнительное управление - будь то автоматическое или ручное, и это более подвержено ошибкам. Наличие таймера в качестве члена класса автоматически привязывает его к времени жизни экземпляра класса, поэтому вы можете избежать всех лишних накладных расходов.

Ответ Пеппе предполагает, что у вас есть член класса QTimer * timer;, которого нет у вас, что является источником дополнительной путаницы.

если я QTimer timer; timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &MainWindow::on_getButton_clicked); я получаю «C2679 оператор не найден, который принимает правый операнд типа QTimer»

Проблема здесь в том, что таймер типа QTimer в то время как new QTimer возвращающую указатель до QTimer, а не QTimer. Очень разные вещи. Затем connect() ожидает указатель на объект, но в вашем случае timer - это экземпляр объекта, а не указатель объекта. Используя оператор &, вы можете получить адрес таймера в памяти или, другими словами, указатель на него.

если я timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &MainWindow::on_getButton_clicked); я получаю «таймера необъявленной идентификатора»

Как говорится, таймер необъявленный, компилятор не знает, что timer потому, что вы не объявили его собственный тип. Теперь, если вы делаете QTimer * timer, компилятор знает, что timer имеет тип указателя на таймер.

+1

Отличный ответ. Если у вас есть время, чтобы добавить небольшое объяснение, объясняющее, почему ваш подход к члену класса лучше, для новичков было бы здорово получить более полный ответ, а не только рабочий код. +1 – Yannis