2016-10-06 2 views
2

J'ai du mal à comprendre comment utiliser le QTimer pour que je répétition une action spécifique (clic d'un bouton ou une demande réseau).Comment utiliser un QTimer pour répéter une action spécifique

Après la answer de cette belle question, je ne peux pas comprendre comment connecter le signal QTimer::Timeout de la minuterie au signal MainWIndow::request de la même manière que je le bouton actuellement connecté. Le problème est que je ne peux pas le faire répéter; il compile et fonctionne sans erreur mais sans répétition.

(comme la personne qui a posté la question, moi aussi peut avoir la répétition de minuterie si je mets tout mon code dans le main.cpp mais je voudrais voir exactement comment il peut être fait dans ce cas.)

Ci-dessous est un code simplifié tiré de cet exemple pour montrer mes tentatives:

#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 // 
} 

mise à jour

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 

Comme il peut être vu dans la section des commentaires, j'ai également essayé dans mainwindow.h pour remplacer l'emplacement privé pour le bouton avec un onTimeout et dans le mainwindow.cpp avant la connexion, j'ai créé un objet QTimer et l'ai démarré et juste après que j'ai utilisé cet objet pour définir le connect(&timer,&QTimer::timeout,this,&MainWindow::onTimeout)‌​;. De plus, j'ai remplacé la définition de l'emplacement du bouton à la fin par le code associé à onTimeout.

Si vous pouvez expliquer ce que je manque ou même fournir le code pour l'exemple exact comme vu here ce serait génial pour moi de comprendre.

+1

Vous créez le temporisateur en tant que local dans le constructeur, ce qui signifie que le temporisateur est détruit après le retour du constructeur. Allouez-le dynamiquement 'QTimer * timer = new QTimer' ou mieux - faites du minuteur un membre de la classe. – dtech

+0

Je doute beaucoup que vous voulez que le minuteur clique sur les boutons ou quoi que ce soit du genre. Vous voulez que la minuterie appelle n'importe quelle action connectée au clic du bouton. C'est assez différent. –

Répondre

2

Le fragment de code ci-dessous illustre l'approche correcte basée sur le code que vous avez fourni:

#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 // 

} 

Vous devriez faire un peu de lecture plus loin pointeurs et le differences avec l'opérateur point (.).

Remarque

réponse de ddriver est préférable à l'utilisation minuterie comme un membre de la classe; cela évite la localité de la référence du pointeur comme dans mon approche.

+1

L'approche de ddriver est meilleure avec l'utilisation de la minuterie en tant que ** membre de classe **, mais peut-être un peu avancée pour vous étant donné votre niveau (pas d'infraction). Ce serait bien si vous pouviez comprendre pourquoi l'approche des membres de la classe est optimale; devoirs de suivi :) – Yannis

+0

C'est exactement ce que je voulais, merci de l'écrire clairement pour moi même si ddriver suggéré cela sur ses commentaires. Je vais maintenant essayer de comprendre l'approche des membres de la classe, merci –

+0

Il est important de noter que ce code vous fera perdre votre minuterie après le retour du constructeur. Cela signifie que vous n'aurez aucun moyen de l'arrêter ou de le modifier de quelque façon que ce soit. La seule référence à la minuterie est son pointeur, qui est un constructeur local, c'est pourquoi c'est une bonne idée d'avoir le pointeur ou le minuteur lui-même en tant que membre de la classe. – dtech

2

Si votre code compile même, cela signifie qu'il est très très probable que vous ayez un membre QTimer *timer dans votre classe. Vous pouvez le déduire ces lignes:

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

Si vous ne l'avez pas, puis l'ajouter à la classe:

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

Cependant, dans votre constructeur de classe, vous créez une autre variable appelée timer, l'observation membre de données:

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

Vous ne voulez certainement pas d'introduire une nouvelle variable là-dedans. De plus, cette variable a stockage automatique, ce qui signifie qu'elle va être détruite à la fin de sa portée englobante (qui est le constructeur lui-même: donc quand vous revenez du constructeur, cet objet timer a disparu).

Solution: ne déclarez pas une nouvelle variable, utilisez le membre de données existant. Changer votre constructeur à quelque chose comme:

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

A propos des déclarations connect réelles.

Celui-ci est tout simplement faux:

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

Il compilera, mais émettent des avertissements lors de l'exécution. Débarrassez-vous de cela. (Comment vous venez retrouviez taper même celui-ci si vous utilisez Qt Creator vous aurait autocomplétion pour le signal et les macros SLOT ...?)

Celui-ci:

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

a une syntaxe correcte, mais je ne peux pas dire si cela fonctionnera réellement, avez-vous une fonction de membre void timer_buttonClicked() marqué comme emplacement? L'autre connect utilise une autre fonction.

Et si vous utilisez Qt 5, passer le plus tôt possible à:

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

qui échoue au moment de la compilation si quelque chose ne va pas.

+0

J'ai essayé ce que vous avez suggéré mais j'ai des erreurs; ** si je fais ** 'Minuterie QTimer; timer = nouveau QTimer (this); connect (timer, & QTimer :: timeout, this, et MainWindow :: on_getButton_clicked); 'I get 'C2679 aucun opérateur trouvé qui prend un opérande droit de type QTimer'. ** si je le fais ** 'timer = new QTimer (this); connect (timer, & QTimer :: timeout, this, & MainWindow :: on_getButton_clicked); 'Je reçois 'identifiant non déclaré par minuterie'. * J'ai mis à jour ma description pour montrer mes emplacements * – Karim

+0

@Karim - c'est toujours une bonne idée de connaître la langue avant d'essayer de l'utiliser en pratique. – dtech

+0

@ddriver Je comprends que je pose peut-être des questions stupides, mais ici, je demande, en lisant et en cherchant des conseils pour «apprendre la langue» comme vous le dites. Désolé si cela vous agace. – Karim

2

Je dirais que la solution la plus simple serait de simplement faire la minuterie d'un membre de la classe, il n'y a vraiment pas besoin d'utiliser l'allocation de tas:

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() { ... } 
}; 

Il est recommandé d'éviter l'allocation de tas à moins que vous savez que ce De quoi as-tu besoin. Et dans ce cas, vous n'en avez pas besoin. Les raisons de l'éviter sont nombreuses: plus lent, plus gourmand en mémoire, échec en cas de mémoire insuffisante, il nécessitera une gestion supplémentaire, automatique ou manuelle, plus sujette aux erreurs. Avoir le minuteur en tant que membre de classe l'attachera automatiquement à la durée de vie de l'instance de classe, de sorte que vous pouvez éviter tous les frais inutiles.

La réponse de Peppe suppose que vous avez un membre de classe QTimer * timer; que vous n'avez pas, ce qui est la source de la confusion supplémentaire.

si je QTimer timer; timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &MainWindow::on_getButton_clicked); je reçois « C2679 aucun opérateur trouvé qui prend un opérande à droite de type QTimer »

Le problème est que la minuterie ici est de type QTimer tandis que new QTimer retourne une pointeur à QTimer, pas un QTimer. Des choses très différentes. Alors connect() attend un pointeur d'objet, mais dans votre cas timer est une instance d'objet, pas un pointeur d'objet. En utilisant l'opérateur &, vous pouvez obtenir l'adresse de la minuterie en mémoire, ou en d'autres termes, un pointeur vers elle.

si je timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &MainWindow::on_getButton_clicked); je reçois « identifiant de minuterie noir »

Comme il est dit, la minuterie est noir, le compilateur ne sait pas ce que timer est parce que vous ne l'avez pas déclaré avec un bon type. Maintenant si vous faites QTimer * timer le compilateur sait que timer est de type pointeur à un temporisateur.

+1

Bonne réponse. Si vous avez le temps d'ajouter une petite explication mentionnant pourquoi votre approche de membre de classe est meilleure, ce serait génial pour les débutants d'obtenir une réponse plus complète et pas seulement le code de travail. +1 – Yannis