Qt 5 Blueprints
上QQ阅读APP看书,第一时间看更新

Creating a basic digital clock

It's time to create a new project, so we will create a Qt Widgets application named Fancy_Clock.

Note

We won't utilize any Qt Quick knowledge in this chapter.

Now, change the window title to Fancy Clock or any other name that you like. Then, the main window UI needs to be tailored because the clock is displayed at the top of the desktop. The menu bar, status bar, and toolbar are all removed. After that, we need to drag an LCD Number widget into centralWidget. Next, change the layout of MainWindow to LayOut Horizontally in order to autoresize the subwidget. The last thing that needs to be done to the UI file is to change frameShape to NoFrame under the QFrame column in the property of lcdNumber. If you've done this right, you'll get a prototype of a digital clock, as shown here:

Creating a basic digital clock

In order to update the LCD number display repeatedly, we have to make use of the QTimer class to set up a timer that emits a signal repetitively. In addition to this, we need to create a slot to receive the signal and to update the LCD number display to the current time. Thus, the QTime class is also needed. This is how the header file of MainWindowmainwindow.h will look now:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
  class MainWindow;
}

class MainWindow : public QMainWindow
{
  Q_OBJECT

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

private:
  Ui::MainWindow *ui;

private slots:
  void updateTime();
};

#endif // MAINWINDOW_H

As you can see, the only modification made here is the declaration of a private updateTime slot. As usual, we're supposed to define this slot in mainwindow.cpp, whose content is pasted here. Note that we need to include QTimer and QTime.

#include <QTimer>
#include <QTime>
#include "mainwindow.h"
#include "ui_mainwindow.h"

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

  QTimer *timer = new QTimer(this);
  connect(timer, &QTimer::timeout, this, &MainWindow::updateTime);
  timer->start(1000);

  updateTime();
}

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

void MainWindow::updateTime()
{
  QTime currentTime = QTime::currentTime();
  QString currentTimeText = currentTime.toString("hh:mm");
  if (currentTime.second() % 2 == 0) {
    currentTimeText[2] = ' ';
  }
  ui->lcdNumber->display(currentTimeText);
}

Inside the updateTime slot, the QTime class is used to deal with the time, that is, the clock. This class can provide accuracy of up to 1 millisecond, if the underlying operating system supports it. However, QTime has nothing to do with the time zone or daylight saving time. It is, at least, sufficient for our little clock. The currentTime() function is a static public function, which is used to create a QTime object that contains the system's local time.

As for the second line of the updateTime function, we used the toString function provided by QTime to convert the time to a string, and then saved it in currentTimeText. The arguments that are passed to toString are in the format of the time string. The full list of expressions can be obtained from Qt Reference Documentation. The colon in the middle of the clock should be flashing, just as in the case of a real digital clock. Hence, we used an if statement to control this. The colon will vanish when the second's value is even, and it will reappear when the second's value is odd. Here, inside the if block, we used the [2] operator to get a modifiable reference of the third character because this is the only way to do direct modifications to a character inside a string. Here, the counting of the currentTimeText string starts from 0. Meanwhile, the at() function of QString returns a constant character, which you have no right to change. At last, this function will let lcdNumber display the time string. Now, let's get back to the constructor of MainWindow. After the initialization of the UI, the first thing it does is to create a QTimer object. Why can't we use a local variable? The answer to that question is because the local variables will be destroyed after the construction of MainWindow. If the timer has gone, there's no way to trigger updateTime repetitively. We don't use a member variable because there is no need to perform the declaration work in the header file, since we won't use this timer elsewhere.

The QTimer class is used to create a repetitive and single-shot timer. It will emit the timeout signal at constant intervals after start is called. Here, we create one timer and connect the timeout signal to the updateTime slot so that updateTime is called every second.

There is another important aspect in Qt called parent-child mechanism. Although it's not as well-known as signals and slots, it plays a crucial role in the development of the Qt applications. Basically speaking, when we create an QObject child with a parent or explicitly set a parent by calling setParent, the parent will add this QObject child to its list of children. Then, when the parent is deleted, it'll go through its list of children and delete each child. In most cases, especially in the design of a UI, the parent-child relationship is set up implicitly. The parent widget or layout automatically becomes the parent object to its children widgets or layouts. In other cases, we have to explicitly set the parent for a QObject child so that the parent can take over its ownership and manage the release of its memory. Hence, we pass the QObject parent, which is this, a MainWindow class to the constructor of QTimer. This ensures that QTimer will be deleted after MainWindow is deleted. That's why we don't have to explicitly write the delete statements in the destructor.

At the end of the constructor, we need to call updateTime explicitly, which will allow the clock to display the current time. If we don't do this, the application will display a zero for a second until the timeout signal is emitted by timer. Now, run your application; it will be similar to the following screenshot:

Creating a basic digital clock