Evgenii Legotckoi
Там. 20, 2015, 6:45 Т.Қ.

Qt/C++ - Сабақ 009. QTimer немесе Qt тілінде таймермен қалай жұмыс істеу керек?

Qt-де QTimer класын пайдалану туралы аздап сөйлесейік. Бұл QSqlTabelModel және оның салдары туралы ұзақ мақалалар сериясынан кейінгі шағын жеңіл тақырып. Ал содан кейін қазірдің өзінде өте сұр зат қайнайды.

Белгілі бір жиілікте TCP/IP стегі арқылы жергілікті желідегі құрылғыларды сұрау немесе деректерді немесе серверге белсенді қосылымдарды сағат сайын тексеру үшін бізге таймер қажет болуы мүмкін. Иә, кез келген нәрсе үшін!? Мұнда QTimer көмекке келеді, біз QLabel-те уақыттың әрбір екінші шығуының мысалын пайдалана отырып қарастырамыз.

Код Qt 5.4.1 негізінде QtCreator 3.3.1-де жазылған.

QTimer үшін жоба құрылымы

Біз жобамызда ең аз файлдарды қолданамыз:

  • QDataMapperWidget.pro - профиль;
  • mainwindow.h – қосымшаның негізгі терезесінің тақырып файлы;
  • mainwindow.cpp – терезенің бастапқы коды;
  • main.cpp – қолданба басталатын негізгі бастапқы файл;
  • mainwindow.ui – қосымшаның негізгі терезесінің формасы;
  • Біз QtCreator дизайнерінде пішінді саламыз. Дегенмен, сурет салатын ештеңе жоқ. QLabel-ді ортаға тастаңыз және сіз аяқтадыңыз.

mainwindow.h

Бұл жобада бақытты болу үшін бізге тек QTimer жұмысына жауап беретін ұяшық және осы сыныптың өзі қажет.

  1. #ifndef MAINWINDOW_H
  2. #define MAINWINDOW_H
  3.  
  4. #include <QMainWindow>
  5. #include <QFont>
  6. #include <QTimer>
  7. #include <QTime>
  8.  
  9. namespace Ui {
  10. class MainWindow;
  11. }
  12.  
  13. class MainWindow : public QMainWindow
  14. {
  15. Q_OBJECT
  16.  
  17. public:
  18. explicit MainWindow(QWidget *parent = 0);
  19. ~MainWindow();
  20.  
  21. /* Будем использовать только один слот */
  22. private slots:
  23. void slotTimerAlarm();
  24.  
  25. private:
  26. Ui::MainWindow *ui;
  27. /* Да сам объект QTimer */
  28. QTimer *timer;
  29. };
  30.  
  31. #endif // MAINWINDOW_H

mainwindow.cpp

Ал енді таймерді іске қосу туралы бірнеше жол. Менің ойымша, кодтан гөрі көп пікірлер бар. Әдетте олар Assembler-де осылай жазады - кодтың 20% және түсініктемелердің 80%.

  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3.  
  4. MainWindow::MainWindow(QWidget *parent) :
  5. QMainWindow(parent),
  6. ui(new Ui::MainWindow)
  7. {
  8. ui->setupUi(this);
  9. /* Немножко подшаманим QLabel, чтобы он был больше,
  10. * и заметнее в пустующем окне
  11. * */
  12. QFont font("Times", 28, QFont::Bold);
  13. ui->label->setFont(font);
  14.  
  15. /* При первом запуске приложения поместим текущее время в QLabel
  16. * */
  17. ui->label->setText(QTime::currentTime().toString("hh:mm:ss"));
  18.  
  19. /* Инициализируем Таймер и подключим его к слоту,
  20. * который будет обрабатывать timeout() таймера
  21. * */
  22. timer = new QTimer();
  23. connect(timer, SIGNAL(timeout()), this, SLOT(slotTimerAlarm()));
  24. timer->start(1000); // И запустим таймер
  25. }
  26.  
  27. MainWindow::~MainWindow()
  28. {
  29. delete ui;
  30. }
  31.  
  32. /* Слот для обработки timeout() таймера
  33. * */
  34. void MainWindow::slotTimerAlarm()
  35. {
  36. /* Ежесекундно обновляем данные по текущему времени
  37. * Перезапускать таймер не требуется
  38. * */
  39. ui->label->setText(QTime::currentTime().toString("hh:mm:ss"));
  40. }

Барлығы

Нәтижесінде, іске қосу кезінде біз қолданба терезесінде секунд сайын уақытты қалай өзгертетінімізді білеміз

QTimer мысалы

Ол саған ұнайды ма? Әлеуметтік желілерде бөлісіңіз!

N
  • Там. 8, 2017, 4:03 Т.Қ.

Добрый день! Появилась проблемка. Есть клиент-серверное приложение. Нужно послать с сервера к клиентам сообщение через определенные промежутки времени.  Реализовал это таким образом, что если у нас в QList лежит больше 1 сокета, то сначало посылается сообщение 1 клиенту, а потом включается таймер, но перед этим записывалось значение i в глобальную переменну(i взято из for). Использовал QTimer::singleshot(2000,this,Slot(slotZ()));. происходит вызов другой функции, где значение сокета берется из списка, по номеру как раз взятого из глобальной переменны, но почему то заместо того чтобы послать на 2 и 3 клиент через 2 сек сообщение, он посылает 2 сообщения на последний клиент.

void MyServer::slotWriteClient()
{
    if(lis.length() != 0)
    {
        QString str;


        for(int i = 0; i < lis.length(); i++)
        {
            socet = lis.at(i);
            if(lineEdit_4->text() == NULL){
                str = QString::number(i + 1) + " Client";
                sendToClient(socet,
                             "Server Response: Received \"" + str + "\""
                             );
            }
            else
            {
                str = lineEdit_4->text();
                
                if(i >= 1)
                {    

                    socn = i;
//                    QTimer *timer = new QTimer(this);
//                    timer->setInterval(2000);
//                    connect(timer, SIGNAL(timeout()), this, SLOT(slotZad(i)));

//                    timer->start();
                    QTimer::singleShot(2000, this, SLOT(slotZad()));

                }
                else
                    sendToClient(socet, str);
            }
        }
    }
    else
        m_ptxt->append("Пуст");
}

void MyServer::slotZad()
{
    QString str;

    qDebug() << socn;
    socet = lis.at(socn);
    str = lineEdit_4->text();
    sendToClient(socet, str);
}
Evgenii Legotckoi
  • Там. 8, 2017, 4:35 Т.Қ.
Добрый день!
Цикл for успевает отработать и сформировать переменную socn до того, как слот slotZad отработает хотя бы раз. При этом цикл for выполнится полностью. В результате в переменной socn будет использоваться переменная i равная последнему индексу.
Вам нужно переделать участок с QTimer::singleShot как-то иначе, например, прерывать цикл for, как только запустили QTimer::singleShot. А в слоте slotZad() инкрементировать переменную socn с проверкой на выход за пределы списка сокетов. И если есть ещё не обработанные сокеты, то запускать заново QTimer::singleShot в слоте slotZad().

Дополнительное code review:
Вместо этого
if(lineEdit_4->text() == NULL){
используйте метод QString::isEmpty()
if(lineEdit_4->text().isEmpty()){

P/S/ В дальнейшем создавайте темы с подобными вопросами на форуме сайта. Вопрос, конечно, имеет отношение к статье, но косвенное.

N
  • Там. 8, 2017, 5:30 Т.Қ.

Спасибо! Учту.

AC
  • Қыр. 7, 2022, 1:40 Т.Ж.

Вижу вы используете
new QTimer();
А кто будет память освобождать?
Почему вы вообще используете указатель QTimer *timer; а не объявите поле QTimer timer;?

Evgenii Legotckoi
  • Қыр. 7, 2022, 1:16 Т.Қ.
  • (өңделген)

Потому что 7 лет назад я был бестолковее, чем сейчас.

f
  • Қыр. 8, 2022, 9 Т.Ж.

QTimer унаследован от QObject и ему передан this, идиома Qt предпологает что при вызове деструктора обьекта класса MyServer, обьект *timer тоже будет освобожден. Поправьте если ошибаюсь!

Evgenii Legotckoi
  • Қыр. 12, 2022, 1:04 Т.Қ.

Да, именно так. Но в коде без this написано - это ошибка в статье.

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз