Build your very first Ubuntu SDK app

Slashdot it! Delicious Share on Facebook Tweet! Digg!

Getting Sleepy

Now that you have a firm understanding of this generated program and the core fundamentals of QML under your belt, I'll show how to make this app into something useful. In this case, I will create a simple sound board that can play a range of different atmospheric sounds to help babies, children, and adults go to sleep.

To begin, in sleepy.qml , remove the entire Column block and set the title of the Page component to Sleepy . When you run the app now, you should see an empty window other than the Sleepy header. This has been removed because I'm going to use a grid to show the sounds instead.

The sounds will be displayed as a bunch of Ubuntu-themed squares, each of which has an icon and some text and, when you click on each rectangle, the sound will play and the square will change color to indicate the sound is playing. With this app, you will be able to mix sounds together if multiple buttons are pressed.

To achieve this outcome, you need to roll your own sound button component in much the same way you did with HelloComponent . To do this, click File | New File or Project… and, in the left panel, click Qt and then QML File (Qt Quick 2) . Call the file SoundButton.qml (case-sensitive) and add it to the project.

Now, you can add the code in Listing 3 to the source file. Although this may look like a lot of code, much of it should look familiar based on what you've learned so far. I'll go through it step by step.

Figure 2: The Ubuntu Component set includes a wide range of components, visible from the Component Showcase.

Listing 3

sleepy.qml – Part 1

01 import QtQuick 2.0
02     import QtMultimedia 5.0
03     import Ubuntu.Components 0.1
05     UbuntuShape {
06         id: box;
07         antialiasing: true;
08         radius: "medium"
10         property alias color: box.color;
11         property alias description: label.text;
12         property alias imageSource: image.source;
13         property alias soundSource: sound.source;
14         property var state: false;
16         Image {
17         id: image;
18         anchors.horizontalCenter: parent.horizontalCenter;
19         anchors.verticalCenter: parent.verticalCenter;
20         width: parent.width * 0.8;
21         height: parent.height * 0.8;
22         fillMode: Image.PreserveAspectFit
23         }
25         Label {
26             id: label;
27             anchors.horizontalCenter: image.horizontalCenter
28    image.bottom
29             text: "Hello, world!"
30             fontSize: "medium"
31         }
33         Audio {
34         id: sound;
36         onStopped: {
37             box.color = "#32222C"
38             if (box.state == true) {
39                 box.color = UbuntuColors.warmGrey
41             }
42         }
43         }
44         MouseArea {
45         anchors.fill: parent;
46         onPressed: {
47             if (box.state == false) {
48                 box.state = true;
49       ;
50                 box.color = UbuntuColors.warmGrey
52             }
53             else if (box.state == true) {
54                 box.state = false;
55                 sound.stop()
56                 box.color = "#32222C"
57             }
59         }
61         }
62     }

First, you add the imports, but there is one new import here: Qt.Multimedia , which provides a range of audio and video features. This is included to be able to play audio files when the user clicks on the sound buttons.

Next, you create the SoundButton , which comprises an UbuntuShape and within it an Image and a Label underneath it. Inside the Image component are some properties that anchor the image to the center of the UbuntuShape .

Next, the width and height settings add a small border around the edge of the image. In the Label , you set the text to some text that will be replaced later, then set the font size and the alignment.

Playing Audio

Now, I'll show how the audio in this component works. When the user clicks the button, you want not only to play the sound but also to stop playing the sound when the user clicks it again. As such, you need to track the state of whether the sound is currently playing or not. A common use case for this app is playing a sound all night while sleeping; to do that, you'll want to loop each sound so it doesn't stop when the sound has finished playing.

Start by adding the Audio component as part of the UbuntuShape , which doesn't do much other than identify that audio can work as part of this component. You may have noticed this line in the top-level UbuntuShape properties:

property var state: false;

This is the variable you will use to track whether the audio of each instance of the sound button is playing. I set this to false by default as the sound is not playing when the component is created.

Now I'll look at how playback works. To begin, create an Audio component that doesn't include much inside it. The presence of the component in UbuntuShape means that this component has the ability to play music.

Inside Audio is an onStopped signal handler for the Audio stopped signal that is fired when either an audio file is stopped with the stop() function or reaches the end of the file and thus stops playback.

In onStopped , check whether the playing state is set to true and, if it is, change the color of the box to UbuntuColors.warmGrey (part of the official Ubuntu color palette), which indicates the sound is playing.

Then, run the play() function to play it. This will handle the case of looping the audio (if the state was set to true , then you know the audio was already playing and as such needs to loop).

Next, I'll take a look at the last component, the MouseArea . This component provides a clickable area throughout the entire UbuntuShape , and each of its children.

Here, you respond to the pressed signal in onPressed and check whether the state of playback is false . If it is (no sound is playing), run the play() function, set the color of the UbuntuShape to warm grey, and set the state to true . Conversely, if the state is true , run stop() to stop playback, set the color to the non-playback color, and set the state to false .

Before adding SoundButtons to the main UI, first create a media/ folder in the sleepy project directory and add some icons and sound files.

I have added the following, for example:

ls media/
bigwaves.png bigwaves.mp3 calmwaves.mp3 calmwaves.png \
  chimes.png chimes.mp3 city.png city.mp3 countryside.png \
  countryside.mp3 whitenoise.png whitenoise.mp3

Here, you can see that I have the same file name for each .png and .mp3 combination. You can assemble these in your project directory with your normal file manager.

Now, you can update sleepy.qml with the SoundButtons . Add the content of Listing 4 inside your Page component. Here, you create an Item component with a Grid inside it. First, in the Item , do a little math to calculate the size of the buttons based upon the number of columns (3) and rows (2) you want. Inside the Grid , you then create each of the SoundButtons , which reference the QML from earlier.

Listing 4

sleep.qml – Part 2

01 Item {
02             anchors.fill: parent
04             id: wrapper
05             property int n_columns: height > width ? 2 : 3;
06             property int n_rows: height > width ? 3 : 2;
07             property int button_size: Math.min (width / n_columns,
08 height / n_rows) * 0.9;
09             property int button_radius: 10;
10             property int button_xspacing: (width - button_size *
11 n_columns) / (n_columns + 1);
12             property int button_yspacing: (height - button_size *
13 n_rows) / (n_rows + 1);
15             Grid {
16                 x: wrapper.button_xspacing;
17                 y: wrapper.button_yspacing;
18                 columns: wrapper.n_columns;
19                 rows: wrapper.n_rows;
21                 columnSpacing: wrapper.button_xspacing;
22                 rowSpacing: wrapper.button_yspacing;
24                 SoundButton {
25                     width: wrapper.button_size;
26                     height: wrapper.button_size;
27                     radius: wrapper.button_radius;
28                     color: "#32222C"
29                     description: "White Noise"
30                     imageSource: "media/whitenoise.png";
31                     soundSource: "media/whitenoise.mp3";
32                 }
33                 SoundButton {
34                     width: wrapper.button_size;
35                     height: wrapper.button_size;
36                     radius: wrapper.button_radius;
37                     color: "#32222C"
38                     description: "Wind Chimes"
39                     imageSource: "media/chimes.png";
40                     soundSource: "media/chimes.mp3";
41                 }
42                 SoundButton {
43                     width: wrapper.button_size;
44                     height: wrapper.button_size;
45                     radius: wrapper.button_radius;
46                     color: "#32222C"
47                     description: "Big Waves"
48                     imageSource: "media/bigwaves.png";
49                     soundSource: "media/bigwaves.mp3";
50                 }
51                 SoundButton {
52                     width: wrapper.button_size;
53                     height: wrapper.button_size;
54                     radius: wrapper.button_radius;
55                     color: "#32222C"
56                     description: "Calm Waves"
57                     imageSource: "media/calmwaves.png";
58                     soundSource: "media/calmwaves.mp3";
59                 }
60                 SoundButton {
61                     width: wrapper.button_size;
62                     height: wrapper.button_size;
63                     radius: wrapper.button_radius;
64                     color: "#32222C"
65                     description: "City Ambiance"
66                     imageSource: "media/city.png";
67                     soundSource: "media/city.mp3";
68                 }
69                 SoundButton {
70                     width: wrapper.button_size;
71                     height: wrapper.button_size;
72                     radius: wrapper.button_radius;
73                     color: "#32222C"
74                     description: "Country Ambiance"
75                     imageSource: "media/countryside.png";
76                     soundSource: "media/countryside.mp3";
77                 }
78             }
79         }

Inside each SoundButton , you pass a description that sets the text of the Label in the SoundButton . You also pass an imageSource and soundSource that map to the aliases in the SoundButton component, which in turn set Image.source and Audio.source .

Now when you run the app, you should see the buttons, be able to start and stop sounds, and have everything work as expected.

Before you finish, you might want to add one final flourish: a nice gradient behind the MainView. To do this, you can add the following lines after height: in MainView :

backgroundColor: "#741266"
    footerColor: "#bd0776"

You will now see a nice smooth gradient behind your app.

Figure 3: The final Sleepy app running, complete with icons and gradient background.

Buy this article as PDF

Express-Checkout as PDF

Pages: 6

Price $0.99
(incl. VAT)

Buy Ubuntu User

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content