Welcome to the world of QML - the declarative UI language. In this Getting Started guide, we will create a simple text editor application using QML. After reading this guide, you should be ready to develop your own applications using QML and Qt C++.
The application we are building is a simple text editor that will load, save, and perform some text manipulation. This guide will consist of two parts. The first part will involve designing the application layout and behaviors using declarative language in QML. For the second part, file loading and saving will be implemented using Qt C++. Using Qt's Meta-Object System, we can expose C++ functions as properties that QML elements can use. Utilizing QML and Qt C++, we can efficiently decouple the interface logic from the application logic.
[Missing image qml-texteditor5_editmenu.png]
To run the QML example code, merely provide the included qmlviewer tool with the QML file as the argument. The C++ portion of this tutorial assumes that the reader possesses basic knowledge of Qt's compilation procedures.
Tutorial chapters:
We start our text editor by building a button. Functionally, a button has a mouse sensitive area and a label. Buttons perform actions when a user presses the button.
In QML, the basic visual item is the Rectangle element. The Rectangle element has properties to control the element's appearance and location.
import Qt 4.7 Rectangle { id: simplebutton color: "grey" width: 150; height: 75 Text{ id: buttonLabel anchors.centerIn: parent text: "button label" } }
First, the import Qt 4.7 allows the qmlviewer tool to import the QML elements we will later use. This line must exist for every QML file. Notice that the version of Qt modules is included in the import statement.
This simple rectangle has a unique identifier, simplebutton, which is bound to the id property. The Rectangle element's properties are bound to values by listing the property, followed by a colon, then the value. In the code sample, the color grey is bound to the the Rectangle's color property. Similarly, we bind the width and height of the Rectangle.
The Text element is a non-editable text field. We name this Text element buttonLabel. To set the string content of the Text field, we bind a value to the text property. The label is contained within the Rectangle and in order to center it in the middle, we assign the anchors of the Text element to its parent, which is called simplebutton. Anchors may bind to other items' anchors, allowing layout assignments simpler.
We shall save this code as SimpleButton.qml. Running qmlviewer with the file as the argument will display the grey rectangle with a text label.
[Missing image qml-texteditor1_simplebutton.png]
To implement the button click functionality, we can use QML's event handling. QML's event handling is very similar to Qt's signal and slot mechanism. Signals are emitted and the connected slot is called.
Rectangle{ id:simplebutton ... MouseArea{ id: buttonMouseArea anchors.fill: parent //anchor all sides of the mouse area to the rectangle's anchors //onClicked handles valid mouse button clicks onClicked: console.log(buttonLabel.text + " clicked" ) } }
We include a MouseArea element in our simplebutton. MouseArea elements describe the interactive area where mouse movements are detected. For our button, we anchor the whole MouseArea to its parent, which is simplebutton. The anchors.fill syntax is one way of accessing a specific property called fill inside a group of properties called anchors. QML uses anchor based layouts where items can anchor to another item, creating robust layouts.
The MouseArea has many signal handlers that are called during mouse movements within the specfied MouseArea boundaries. One of them is onClicked and it is called whenever the acceptable mouse button is clicked, the left click being the default. We can bind actions to the onClicked handler. In our example, console.log() outputs text whenever the mouse area is clicked. The function console.log() is a useful tool for debugging purposes and for outputting text.
The code in SimpleButton.qml is sufficient to display a button on the screen and output text whenever it is clicked with a mouse.
Rectangle {
id:Button
...
property color buttonColor: "lightblue"
property color onHoverColor: "gold"
property color borderColor: "white"
signal buttonClick()
onButtonClick: {
console.log(buttonLabel.text + " clicked" )
}
MouseArea{
onClicked: buttonClick()
hoverEnabled: true
onEntered: parent.border.color = onHoverColor
onExited: parent.border.color = borderColor
}
//determines the color of the button by using the conditional operator
color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor
}
A fully functioning button is in Button.qml. The code snippets in this article have some code omitted, denoted by ellipses because they were either introduced earlier in the previous sections or irrelevant to the current code discussion.
Custom properties are declared using the property type name syntax. In the code, the property buttonColor, of type color, is declared and bound to the value "lightblue". The buttonColor is later used in a conditional operation to determine the buttons's fill color. Note that property value assignment is possible using the = equals sign, in addition to value binding using the : colon character. Custom properties allow internal items to be accessible outside of the Rectangle's scope. There are basic QML types such as int, string, real, as well as a type called variant.
By binding the onEntered and onExited signal handlers to colors, the button's border will turn yellow when the mouse hovers above the button and reverts the color when the mouse exits the mouse area.
A buttonClick() signal is declared in Button.qml by placing the signal keyword in front of the signal name. All signals have their handlers automatically created, their names starting with on. As a result, the onButtonClick is buttonClick's handler. The onButtonClick is then assigned an action to perform. In our button example, the onClicked mouse handler will simply call onButtonClick, which displays a text. The onButtonClick enables outside objects to access the Button's mouse area easily. For example, items may have more than one MouseArea declarations and a buttonClick signal can make the distinction between the several MouseArea signal handlers better.
We now have the basic knowledge to implement items in QML that can handle basic mouse movements. We created a Text label inside a Rectangle, customized its properties, and implemented behaviors that respond to mouse movements. This idea of creating elements within elements is repeated throughout the text editor application.
This button is not useful unless used as a component to perform an action. In the next section, we will soon create a menu containing several of these buttons.
[Missing image qml-texteditor1_button.png]
Up to this stage, we covered how to create elements and assign behaviors inside a single QML file. In this section, we will cover how to import QML elements and how to reuse some of the created components to build other components.
Menus display the contents of a list, each item having the ability to perform an action. In QML, we can create a menu in several ways. First, we will create a menu containing buttons which will eventually perform different actions. The menu code is in FileMenu.qml.
import Qt 4.7 \\import the main Qt QML module import �folderName� \\import the contents of the folder import �Button.qml� \\import a QML file import �NewButton.qml� as ButtonModule \\import a QML file and give it a name import �script.js� as Script \\import a Javascript file and name it as Script
To use the Button element in FileMenu.qml, we need to import Button.qml. The syntax shown above, shows how to use the import keyword. However, the import Button.qml is not necessary; qmlviewer will import all the contents of the current directory. We can directly create a Button element by declaring Button{}, similar to a Rectangle{} declaration.
In FileMenu.qml: Row{ anchors.centerIn: parent spacing: parent.width/6 Button{ id: loadButton buttonColor: "lightgrey" label: "Load" } Button{ buttonColor: "grey" id: saveButton label: "Save" } Button{ id: exitButton label: "Exit" buttonColor: "darkgrey" onButtonClick: Qt.quit() } }
In FileMenu.qml, we declare three Button elements. They are declared inside a Row element, a positioner that will position its children along a vertical row. The Button declaration resides in Button.qml, which is the same as the Button.qml we used in the previous section. New property bindings can be declared within the newly created buttons, effectively overwriting the properties set in Button.qml. The button called exitButton will quit and close the window when it is clicked. Note that the signal handler onButtonClick in Button.qml will be called in addition to the onButtonClick handler in exitButton.
[Missing image qml-texteditor1_filemenu.png]
The Row declaration is declared in a Rectangle, creating a rectangle container for the row of buttons. This additional rectangle creates an indirect way of organizing the row of buttons inside a menu.
The declaration of the edit menu is very similar at this stage. The menu has buttons that have the labels: Copy, Paste, and Select All.
[Missing image qml-texteditor1_editmenu.png]
Armed with our knowledge of importing and customizing previously made components, we may now combine these menu pages to create a menu bar, consisting of buttons to select the menu, and look at how we may structure data using QML.
Our text editor application will need a way to display menus using a menu bar. The menu bar will switch the different menus and the user can choose which menu to display. Menu switching implies that the menus need more structure than merely displaying them in a row. QML uses models and views to structure data and display the structured data.
QML has different data views that display data models. Our menu bar will display the menus in a list, with a header that displays a row of menu names. The list of menus are declared inside a VisualItemModel. The VisualItemModel element contains items that already have views such as Rectangle elements and imported UI elements. Other model types such as the �{ListModel}{c ListModel} element need a delegate to display their data.
We declare two visual items in the menuListModel, the FileMenu and the EditMenu. We customize the two menus and display them using a ListView. The MenuBar.qml file contains the QML declarations and a simple edit menu is defined in EditMenu.qml.
VisualItemModel{ id: menuListModel FileMenu{ width: menuListView.width height: menuBar.height color: fileColor } EditMenu{ color: editColor width: menuListView.width height: menuBar.height } }
The ListView element will display a model according to a delegate. The delegate may declare the model items to display in a Row element or display the items in a grid. Our menuListModel already has visible items, therefore, we do not need to declare a delegate.
ListView{ id: menuListView //Anchors are set to react to window anchors anchors.fill:parent anchors.bottom: parent.bottom width:parent.width height: parent.height //the model contains the data model: menuListModel //control the movement of the menu switching snapMode: ListView.SnapOneItem orientation: ListView.Horizontal boundsBehavior: Flickable.StopAtBounds flickDeceleration: 5000 highlightFollowsCurrentItem: true highlightMoveDuration:240 highlightRangeMode: ListView.StrictlyEnforceRange }
Additionally, ListView inherits from �{Flickable}{c Flickable}, making the list respond to mouse drags and other gestures. The last portion of the code above sets Flickable properties to create the desired flicking movement to our view. In particular,the property highlightMoveDuration changes the duration of the flick transition. A higher highlightMoveDuration value results in slower menu switching.
The ListView maintains the model items through an index and each visual item in the model is accessible through the index, in the order of the declaration. Changing the currentIndex effectively changes the highlighted item in the ListView. The header of our menu bar exemplify this effect. There are two buttons in a row, both changing the current menu when clicked. The fileButton changes the current menu to the file menu when clicked, the index being 0 because FileMenu is declared first in the menuListModel. Similarly, the editButton will change the current menu to the EditMenu when clicked.
The labelList rectangle has z value of 1, denoting that it is displayed at the front of the menu bar. Items with higher z values are displayed in front of items with lower z values. The default z value is 0.
Rectangle{ id: labelList ... z: 1 Row{ anchors.centerIn: parent spacing:40 Button{ label: "File" id: fileButton ... onButtonClick: menuListView.currentIndex = 0 } Button{ id: editButton label: "Edit" ... onButtonClick: menuListView.currentIndex = 1 } } }
The menu bar we just created can be flicked to access the menus or by clicking on the menu names at the top. Switching menu screens feel intuitive and responsive.
[Missing image qml-texteditor2_menubar.png]