9 min read

Qt provides a different look for mobile and embedded devices where users expect a different style of presentation. This is controlled within the framework so the developer can concentrate on developing a single application.

The Qt framework is released in two separate distributions, one commercial and one open source (known as dual licensing). In this manner, they can support open source-compliant applications for free, while providing unrestricted usage for closed source commercial projects. Before the year 2000 (with the release of 2.2), the source code for the free distribution had been under various licenses that some groups considered incompatible with common open source initiatives. For the 2.2 release, it was changed to GPL licensing, which settled any concerns about the group’s commitment to true open source freedoms. In 2007, Qt 4.5 was released and they added LGPL as an option for developers who prefer the more permissive license.

This article is an excerpt taken from the book Hands-On GUI Application Development in Go. This book covers the benefits and complexities of building native graphical applications, the procedure for building platform and developing graphical Windows applications using Walk. 

This article covers the basics of therecipe/qt, multiple platforms, installation of qt (the bindings), and much more.

Getting started with therecipe/qt

To begin our exploration of Qt and the binding to Go, we’ll build a simple hello world application. To be able to do so, we first need to install therecipe/qt, which depends on various prerequisites that we must first set up. As with Go-GTK, we’ll be relying on a native library that requires that we both set up the CGo functionality and install the Qt library appropriate for the current platform.

Preparing CGo

The Qt Go bindings, like many of the other toolkits featured in this book, require the presence of CGo to utilize native libraries. On a full development system, it’s likely that this is already set up.

Installing Qt

The Qt website offers various methods of installation, including a customized online installer available to anyone with a Qt account (which is free to sign up for). Typically, a Qt installation comes with Qt Creator (the project IDE), the GUI designer, additional tools, and examples. Visiting the preceding site will automatically detect your system and suggest the most appropriate download (this is normally the best option).

Be aware that the Qt installation can be quite large. If you don’t have at least 40 GB of space on your hard drive, you need to make a little space before installing.

Some operating systems offer Qt libraries and tools as part of their package manager, which often provides a more lightweight installation that’ll automatically stay up to date.

Installing Qt on multiple platforms

macOS

On Apple macOS, the best approach to installation is to use the installer application available at the Qt download site. Visit www.qt.io/download and download the macOS installer. Once it has downloaded, open the package and run the program inside; this will install the selected compilers, tools, and supporting applications. If you encounter any errors during installation, the first step would be to check that your Xcode installation is complete and up to date.

Windows

Installing on Windows is more straightforward than some of the other toolkits we’ve looked at, as the Qt installer has a mingw package bundled to set up most of the compiling requirements (though it’s still recommended to have your own compiler set up for the binding phase next). To install it, go to the download page listed previously and access the Windows installer. Run the downloaded executable and follow the onscreen instructions. It’s recommended to install to the default location. Once that’s done, you’re ready to set up the bindings.

Linux

Using the online installer from Qt’s website is the easiest approach, though it may be possible to install through your system’s package manager (if you want to try the package manager approach, then first read the Qt Linux documentation. On most Linux platforms, the Qt downloads website will correctly detect the platform and offer a simple run installer. After downloading the file, you should make it executable and then run it:

On Linux, you need to make the install file executable and run it

This will start the installer just as on macOS; from here, follow the onscreen instructions and complete the installation.

License / Qt account

When it comes to the login screen, then you should enter your Qt account details if you have them. If you qualify for their open source license (GPL or LGPL), you can skip this step—to do so; make sure the email and password fields are empty.

Installing qt (the bindings)

To use qt (the Go Qt bindings), we need to download the project and its dependencies and then run a setup script to configure and compile the library. If using Windows, it’s recommended to use the MSYS2 Terminal.

If you installed the Qt download to anything other than the default location, then make sure to set up the QT_DIR environment variable to the location you chose.

First, the library and its dependencies should be installed using the go tools, by running go get github.com/sirupsen/logrus and go get github.com/therecipe/qt.

Once the download has completed, we need to run the qtsetup tool, which is included in the qt project; so, within the cmd/qtsetup folder, execute go run main.go. Using a Linux Terminal, it should look something like this:

Executing the qtsetup script for therecipe/qt bindings

Once this process completes, the bindings should be ready to use. If you encounter errors, then it’s probably because the Qt tools aren’t correctly installed or the location was customized and you forgot to set the QT_DIR environment variable.

Build

To build our first qt application with Go, let’s make another Hello World application. As with previous examples, we’ll make use of a simple vertical box layout within a single application window. The following code should be sufficient to load your first application:

package main
import (
"os"

"github.com/therecipe/qt/widgets"
)

func main() {
app := widgets.NewQApplication(len(os.Args), os.Args)

window := widgets.NewQMainWindow(nil, 0)
window.SetWindowTitle("Hello World")

widget := widgets.NewQWidget(window, 0)
widget.SetLayout(widgets.NewQVBoxLayout())
window.SetCentralWidget(widget)

label := widgets.NewQLabel2("Hello World!", window, 0)
widget.Layout().AddWidget(label)

button := widgets.NewQPushButton2("Quit", window)
button.ConnectClicked(func(bool) {
app.QuitDefault()
})
widget.Layout().AddWidget(button)

window.Show()
widgets.QApplication_Exec()
}

Let’s note a few details from this code snippet. You’ll see that each of the widget constructor functions takes (typically) two parameters, each is the parent widget and a flags parameter. Additional types passed in will usually be added before these values with a note in the function name that there are additional parameters. For example, widgets.NewQLabel2(title, parent, flags) is equivalent to widgets.NewQLabel(parent, flags).SetTitle(title). Additionally, you’ll see that the layout is applied to a new widgets.QWidget through SetLayout(layout), and that’s set to the window content through window.SetCentralWidget(widget).

To load the display and run the application, we call window.Show() and then widgets.QApplication_Exec(). This file is built in the usual way with go build hello.go:

Building is simple though the output file is rather large

The file built is quite large due to the size of the Qt framework. This will be reduced significantly when packaging for a specific distribution.

Run

The output of the build phase is a binary that can be executed on the current computer, either on the command line or by double-clicking in a file manager. Additionally, you could execute it directly with go run hello.go—either way, you should see a simple window, as shown here:

qt Hello on Linux

Running on macOS

At this stage, the binaries can be executed on a computer with the same architecture that also has Qt installed.

Object model and event handling

The Qt framework is written using the C++ language, and so much of its architecture will be familiar to those who’ve coded in C++ before. It’s important to note that Go isn’t a complete object-oriented language and, as such, doesn’t match these capabilities directly. In particular, we should look at inheritance as it’s important to the Qt object model.

Inheritance

The Qt API is a fully object-oriented model that makes heavy use of the inheritance model. While Go doesn’t truly support object-oriented inheritance in the traditional manner, its composition approach is very powerful and works well in its place. The result means that you probably won’t notice the difference!

Memory management

As you’ll have noticed in the preceding example, each widget expects the parent to be passed to the constructing function. This enables the Qt framework to handle the tidying up and freeing of memory when a tree of widgets is removed. QObject (which is the base object for all of the Qt API) keeps track of its child objects and so, when being removed, can remove its children too. This makes the creation and deletion of complex widget hierarchies easier to handle correctly. To make use of this feature, you should always remember to pass the parent object to a widget’s constructor (the Go functions starting with New...), despite the fact that passing nil may look like it’s working.

Signals and slots

Qt is similar to GTK+, an event-driven framework and uses signals extensively to handle event management and data communications. In Qt, this concept is split into signals and slots; a signal is what will be generated when an event occurs and a slot is what can receive a signal. The action of setting a slot to receive a signal is called connecting and this causes a slot function to be called when its connected signal is invoked. In Qt, these are typed events meaning that each signal has a list of type parameters associated with it. When the signal is defined, this type is set and any slot wishing to connect to the signal will need to have the same type.

s.ConnectMySignal(
   func(msg string) {
      log.Println("Signalled message", msg)
   }
)

Signals and slots are what power user interfaces generated with Qt Designer and are the recommended way of handling multi-threaded applications. A signal may fire from a background thread and the user interface code can connect this signal to its own slot—in essence, listening for the signal. When the signal fires, any associated data (parameters to the signal) will be passed from one thread to another so it can be used safely within the GUI updates.

As Qt is a lightweight binding to the Qt API, the Go-specific documentation is minimal but you can find out a lot more about the Qt design and all of the classes available in the official documentation available at Qt’s blog post.

In this article, we have learned about the Qt framework and the multiple platforms, therecipe/qt, installation of qt (the bindings), and much more. To know more about Go-GTK and platforms with GTK, check out the book Hands-On GUI Application Development in Go.

Read Next

Qt Creator 4.9 Beta released with QML support, programming language support and more!

Qt Design Studio 1.1 released with Qt Photoshop bridge, updated timeline and more

Qt for Python 5.12 released with PySide2, Qt GUI and more