End to End GUI Development with Qt5
上QQ阅读APP看书,第一时间看更新

Designing a cross-platform project

We want to display some visual gauges and chart widgets, so create a new Qt widgets Application called ch02-sysinfo. As already discussed earlier, Qt Creator will generate some files for us: main.cppMainWindow.hMainWindow.cpp, and MainWindow.ui.

Before diving into the C++ code, we must think about the software's architecture. This project will handle multiple desktop platforms. Thanks to the combination of C++ and Qt, most of the source code will be common to all targets. However, to retrieve both the CPU and memory usage from the OS (operating system), we will use some platform-specific code.

To successfully achieve this task, we will use two design patterns:

  • Strategy pattern: This is an interface that describes functionalities (for example, retrieve CPU usage), and specific behaviors (retrieve CPU usage on Windows/Mac OS/Linux) will be performed into subclasses that implement this interface.
  • Singleton pattern: This pattern guarantees a single instance for a given class. This instance will be easily accessible with a unique access point.

As you can see in the following diagram, the class SysInfo is our interface with the strategy pattern, and is also a singleton. The specific behavior from the strategy pattern is performed in the classes SysInfoWindowsImplSysInfoMacImpl, and SysInfoLinuxImpl, subclassing SysInfo:

The UI part will only know and use the SysInfo class. The platform-specific implementation class is instantiated by the SysInfo class, and the caller doesn't need to know anything about the SysInfo child classes. As the SysInfo class is a singleton, access will be easier for all widgets.

Let's begin by creating the SysInfo class. On Qt Creator, you can create a new C++ class from the contextual menu, accessible with a right click on the project name in the hierarchy view. Then click on the Add new option, or from the menu, go to FileNew file or projectFiles and classes. Then perform the following steps:

  1. Go to C++ ClassChoose.
  2. Set the Class name field to SysInfo. As this class does not inherit from another class, we do not need to use the Base class field.
  3. Click on Next, then Finish to generate an empty C++ class.

We will now specify our interface by adding three pure virtual functions: init()cpuLoadAverage(), and memoryUsed():

// In SysInfo.h 
class SysInfo 
{ 
public: 
    SysInfo(); 
    virtual ~SysInfo(); 
 
    virtual void init() = 0; 
    virtual double cpuLoadAverage() = 0; 
    virtual double memoryUsed() = 0; 
}; 
 
// In SysInfo.cpp 
#include "SysInfo.h" 
 
SysInfo::SysInfo() 
{ 
} 
 
SysInfo::~SysInfo() 
{ 
} 

Each of these functions has specific roles:

  • init(): This function allows the derived class to perform any initialization process depending on the OS platform
  • cpuLoadAverage(): This function calls some OS-specific code to retrieve the average CPU load and returns it as a percentage value
  • memoryUsed(): This function calls some OS-specific code to retrieve the memory used and returns it as a percentage value

The virtual keyword indicates that the function can be overridden in a derived class. The = 0 syntax means that this function is pure virtual, and must be overridden in any concrete derived class. Moreover, this makes SysInfo an abstract class that cannot be instantiated.

We also added an empty virtual destructor. This destructor must be virtual to ensure that any deletion of an instance of a derived class--from a base class pointer--will call the derived class destructor and not only the base class destructor.

Now that our SysInfo class is an abstract class and ready to be derived, we will describe three implementations: Windows, Mac OS, and Linux. You can also perform only one implementation if you would rather not use the other two. We will not make any judgment on this. The SysInfo class will be transformed into a singleton after adding the implementations.