📜 ⬆️ ⬇️

Writing a desktop widget under Maemo5 on Qt. Part two and final

Good day, habrapolzovatel. I continue the series of articles on the Nokia N900. And I continue the last time I started an article about writing a widget for the N900 on Qt . And so, last time we made a simple application that retrieves the list from a file of a special format (xml). This time we will finish this application and turn it into such a desktop widget:
image
As I write more about Maemo5 (MeeGo in perspective), I will try to pay less attention to Qt and more to integrate with Maemo5 / Hildon. And I’ll try to show that the development of Qt applications does not require knowledge of the specifics of the platform and the “native environment” (in our case, this is GTK).
In this article I will write about how:
  1. Write a delegate for a “beautiful” display of information.
  2. Implement your own model to ensure the functionality of the application.
  3. Make a composite widget of several other widgets and a set of functionality.
  4. Design it as a desktop widget and make a .deb package.
  5. Add a settings page to this widget.

Most importantly, all of the above does not require much in-depth knowledge and adaptation of the application for Maemo5, the resulting widget can work fine on the desktop system (Mac / Win / Lin), but not as a desktop widget, but as a simple application.


Delegate to display list


First of all, you need to decide what and how to display. Sufficient minimum information:
  1. Departure time.
  2. How much time is left before departure.
  3. Notes (by what numbers, on which days).

Having decided a little, I decided to do this: in large numbers to display the time before departure and the space of departure after a space. I decided to make the color of time before departure dynamically. For example, if less than 5 minutes are left before departure, then it will be painted red, if less than 15 minutes, then dark yellow. If more than 15 minutes before departure, then it is dark blue. It turns out two thresholds. In my example, these are 5 and 15 minutes. It was decided to make these thresholds customizable and make them into settings (platform-specific storage, QSettings ). It would be nice to make the colors themselves customizable, but let it be in “Roadmap”. Immediately after the previous two fields for the rest of the space you need to place a note. Moreover, the height should fit two lines of text from the note, since the note may contain quite a lot of information.
It should look like this in the end:
image
Due to the fact that nobody responded to my request for help in the design of the application interface, I decided to do everything on my own. Since I am not particularly strong in this very design, I decided to put the list elements to the maximum on the system. Because of this, the delegate code that is responsible for drawing this list item has become a bit complicated and needs additional description (but it was possible to demonstrate style-independent drawing of the widget).

To implement a delegate, you need to block only two methods (since our delegate does not modify the data):
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const ;
QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex& index) const ;


* This source code was highlighted with Source Code Highlighter .

The first method draws an element, and the second method returns the required dimensions for drawing a single element. Well, actually the delegate itself is a successor to QStyledItemDelegate. More information about the deregs can be found in the official documentation. Due to the fact that we inherited from the stylized delegate, in both of the above-mentioned methods in the form of parameters, we get the QStyleOptionViewItem variable. Thanks to this variable, you can "reach out" to the parameters of the current style. Here are some examples:

There are a lot of different information available in Qt and I would recommend to get acquainted with the system of drawing widgets using styles to understand how and what is used.
As you can see from my code, to determine the font size for drawing time and notes, I used the static constant fontDelta. It is 3 on the desktop and 6 on the Memo 5 device (N900). This is a very “dirty” workaround or, in the common people, a nail. It is connected with the fact that on the N900 with a delta 6 two lines of notes are placed in one time line, and on the desktop system with a delta equal to 3. Here is an example of incorrect drawing:
image
But if a system appears for which the delta is already different, then you will need to add another value to the delta, which is not very good. Therefore, this delta must be calculated programmatically (using QFonrMetrics for example). So this implementation is draft and timed. However, as a result, we received a delegate who drew the following on the screen:
image
I believe that the result was readable and beautiful. The lines are highlighted with each other (even colors have one color, and odd ones have another). And the colors for the substrate of even and odd lines correspond to the system ones (system colors can be seen in the background in the debugger window).
')

Controls


Since we can have several schedules (at least you need to have two schedules: “there” and “back”), you need to be able to switch between them. For this, I thought about two arrows: “forward” and “backward” in the list of schedules. To know what schedule is currently displayed, you need to put on the widget text with the name of the direction. In the course of a 4-minute design, I got the following widget:
image
Clicking on the left and right arrows switches to the previous and next schedule in the list of schedules.
On clicking on the inscription with the direction - the list is positioned on the nearest poisonous train.
Looking ahead, I’ll tell you that the widget on the desktop “not correctly” intercepts the scrolling and dragging event. Therefore, the list of scrolling did not work either using the scroll bar or using the usual scrolling with your finger (touched and stretched up or down). The problem was solved in this way: if you click on an item in the list that is above the middle of the displayed list, the list will “turn” in such a way that this item will be the last in the visible section of the list (Scroll Up):
image
And if you click on an element that is below the middle of the visible list, it will be the first in the visible list, respectively (Scroll Down):
image
Well, the update time remaining before departure is updated by timer every minute. And the list itself updates the data from the network when it is loaded or when the settings window is closed.

Model


The model has been modified slightly from the previous state. A list of schedules was added, as it became possible to manipulate multiple schedules. It would be better for each individual schedule to instantiate its own model, and in the widget to switch between them. But since I initially hid the loading mechanism “in the model” and everything connected with it there, it was more logical and easier to transfer the list of timetables to the model. Therefore, I ask you not to criticize strongly for such a deviation from the template (especially since in this particular example there are practically no “losses” from this “deviation”).

Run as a normal application


image
If you run the resulting code at this stage, we get the application. Not having any mention of widgets and Maemo5. The result of the implementation on the N900 you see above.
This is how the application looks on Mac OS:
image
In the simulator for push-button symbian phones on the example of N95, the result looks like this:
image
Well, for tacho simbian on the example of N97 like this:
image
The result was good. Under each system, the list is displayed in "native" colors, although it is completely drawn by our code. Moreover, if you change the style on the device, the list will change accordingly. Which is very cool, since it will make the widget "native" for any style you set.

Start


This is where you can start writing an article. Everything that happened before has nothing to do with mobile development. This is an introduction. But I wanted to show the whole cycle of writing an application for the N900, to show that the development for the N900 (or another Qt platform, Symbian for example) is no different from developing a desktop application on Qt. Moreover, developing a Qt application for mobile platforms is for the most part a regular Qt development.
The application that we have now is not tied to any platform. It can be built and run on any supported platform without changing the code at all.

Identification


In some cases, it is necessary to “understand” in the code under which platform and with what parameters the application is assembled. And in Qt code, you can use the following preprocessor definitions: Q_WS_XXXXXX. It stands for “Qt Window System”. For Maemo, it will sound like this: Q_WS_MAEMO_5. In the .pro file, there are branch commands for each platform: win, mac, maemo5 ... This also needs to be used to identify the platform, if you need to use code specific to that platform. In order to understand that we are collecting code for Maemo5 and the application is assembled as a desktop widget, the code uses the standard Q_WS_MAEMO5 directive and the introduced RASP_WIDGET directive. If there is no last directive, the project is assembled as a simple application for the Maemo5 platform.

Creating a widget


image
There is a special project that is an adapter implemented on the basis of the “bridge” pattern. On the one hand, this is the Hildon interface of the desktop widget, and on the other, it is the QObject for interacting with Qt code. The QMaemo5HomescreenAdaptor is called and the package with the source code and an example can be downloaded here. All you need to do is add the section to a .pro file of approximately the following content:
maemo5: contains (DEFINES, RASP_WIDGET): include (./ qmaemo5homescreenadaptor / qmaemo5homescreenadaptor.pri)

That is, if the project is built for maemo5 and the RASP_WIDGET preprocessor directive is specified, then add the nested project of the desktop adapter to the project.
And in the main.cpp file we add the following code:
#elif defined(Q_WS_MAEMO_5) && defined(RASP_WIDGET)
QMaemo5HomescreenAdaptor *adaptor = new QMaemo5HomescreenAdaptor(&w);
adaptor->setSettingsAvailable( true );
QObject::connect(adaptor, SIGNAL(settingsRequested()), &w, SLOT(showSettingsDialog()));
w.show();
#elif defined(Q_WS_MAEMO_5)
w.showMaximized();
#else
w.show();
#endif

* This source code was highlighted with Source Code Highlighter .


Everything is simple: if we assemble a widget, then we create an adapter and connect the signal about calling the settings window to the corresponding slot of the widget. In the case of building a project as an application for Maemo5 (not a widget), the adapter is not created, and the widget is displayed on the full screen.

And you also need to remember to add the adapter header file in the header of the main.cpp file:
#if defined(Q_WS_MAEMO_5) && defined(RASP_WIDGET)
#include "qmaemo5homescreenadaptor.h"
#endif


* This source code was highlighted with Source Code Highlighter .


That's all. All code to implement the widget is written. But just so you can not start it as a widget, for this you need to install in the system as a desktop widget. And for this you need to collect .deb package.

Build the widget in .deb package


This part is the most dreary and complex. Since I could not debug the application on the device as a widget, the final result can be checked only by assembling the package and installing it on the device.
Based on this very useful article .
Now step by step. To execute all the commands you need to register the path to the binaries from MADDE. Under Windows, it is enough to run a special terminal available in the Nokia Qt SDK after installation. Under * nix systems, you just need to add the path to the PATH variable:
export PATH = ~ / NokiaQtSDK / Maemo / 4.6.2 / bin /: $ PATH

The environment can be considered customized.
1. Create a directory

Next, create a separate directory with the name <application name> - <version number>. And strictly in this form. In my case, it turns out: "rule-raspisanie-widget-0.1." This is exactly what the resulting application will be called in the package manager. After that we drag there all that is needed to build the application: .pro, .cpp, .h, .qrc ,.
2. Create a .desktop file

In my case, this is the content of this file:
[Desktop Entry]
Name = Qt desktop timetable
Comment = Qt based widget example for habrahabr
Type = qt
X-Path = / opt / rule-raspisanie-widget / raspisanie
X-Multiple-Instances = false
X-home-applet-minwidth = 200
X-home-applet-minheight = 200

There is no need for special comments. The main thing here is to specify the path to the executable file - "X-Path" and disable the ability to run multiple instances of the application - "X-Multiple-Instances". More information about the file format can be found here and here .
3. Modify the .pro file

This .desktop file must be copied to a specific folder on the system during installation, so you need to add this rule to the .pro file in order to generate the corresponding rule in the Makefile.
Add to the .pro file in the section with maemo5 these are the lines here:
PREFIX = debian / rule-raspisanie-widget
desktop.path = $$ PREFIX / usr / share / applications / hildon-home
desktop.files = * .desktop

target.path = $$ PREFIX / opt / rule-raspisanie-widget /
INSTALLS + = target desktop

Here the prefix is ​​the root of the system in the .deb package. The above code takes two steps:

Adding new build steps to the .pro file is quite a powerful tool, and I recommend to get to know it better .
4. Generate the skeleton for the .deb package

In this step, MADDE will do everything for us, all that needs to be done is to execute the following command in the created directory:
mad dh_make -createorig -single -ea@example.com -c gpl

For more information about packaging using MADDE can be found in the official documentation .
After that, the “debian” directory will appear with a bunch of files inside. We will need to correct some of them.
control file

This file contains all the information needed to build and install the application.
This is how this file looks like:
Source: rule-raspisanie-widget
Section: user / desktop
Priority: extra
Maintainer: Ievgen Rudenko <erule.biz@gmail.com>
Build-Depends: debhelper (> = 5), libqt4-dev (> = 4.6.1), libhildon1-dev, libhildondesktop1-dev
Standards-Version: 3.7.3
Homepage: < erudenko.com , rule.habrahabr.ru >

Package: rule-raspisanie-widget
Architecture: any
Depends: $ {shlibs: Depends}, $ {misc: Depends}, qt4-homescreen-loader
Description: The test widget for the desktop, shoew timetable
It's a Qt example of making Desktop Widget for Maemo.
It's easy to use XML format to retrieve via web.

Important parameters are:

And of course, do not forget to fill in the fields with the name, maintainer and description.
file "rules"

This file describes the steps for building a project. It is necessary to prescribe in it that you first need to run qmake before executing make (to generate a Makefile) at the build stage and the same for the cleanup stage.
We are looking for the rule build-stamp: configure-stamp and edit (bold highlights what needs to be inserted):
build-stamp: configure-stamp
dh_testdir

# Add commands to compile the package.
qmake "DEFINES + = RASP_WIDGET" && $ (MAKE)

Do not forget about the Makefile format: before each step, you need to put a tab character to assemble the target .
Now we change the cleaning stage, look for the goal of the same name - clean in the rules file and change it accordingly. After the change, this stage will look like this:
clean:
dh_testdir
dh_testroot
rm -f build-stamp configure-stamp

# Add here commands to clean up after the build process.
qmake "DEFINES + = RASP_WIDGET" && $ (MAKE) clean

dh_clean


file "postinst"

This file is executed after the package is installed in the system and additional commands can be written here, if necessary. In our case, you need to bypass one small bug in MADDE, which is that the application “flushes” the “x” flag in the application informing that the file is executive. All you need to do is write a command in the file to set this flag:
#! / bin / sh
chmod + x / opt / rule-raspisanie-widget / raspisanie

Thus, after installation, this script will run, which will set the "x" flag to our application.

compat file

According to the documentation , this is a version file to check for compatibility with Debhelper: “Debian helper compatibily version.” Just make sure that there is “5”, not “7”.

5. Build .deb package

Everything is ready, all you need to do is execute the command:
mad dpkg-buildpackage -us -uc

And in the directory above "../" you will find three files with the package name and extensions: dsc, tar.gz, changes, deb. Here we need the last one.
PS: You can read more about the above-described "shamanism" in the documentation for creating .deb packages .

Installing the resulting package


I did it simply - I copied c using scp to the device and ran it from the standard file manager directly on the device. After that, the package was installed and you just need to add the widget to your desktop in the usual way and everything will work. You can also send it to yourself in attachments, and if you double-click on the attached .deb package on the device, the package manager will automatically open and install the package from the message attachment.
You can also do this using the “dpkg -i” commands on the device via ssh or MADDE Device Runtime .

Rebuilding .deb package


Here, too, everything is simple: delete the generated files dsc, tar.gz, changes, deb. Copy the new sources over the old ones in the package build folder and execute the command again:
mad dpkg-buildpackage -us -uc

We delete our application with the help of the package manager and install the new package in one of the above ways.

findings


You can see the result at the very top, this is a screenshot from my N900. The application is not perfect, and clearly needs further development, but it is efficient and performs the basic functions. I consider the main task (of writing an application without knowledge of the platform) accomplished. The only thing that had to be tricky was packing into a .deb file. But first, this package format is not Maemo specific. And secondly, in the next releases of MADDE, all these steps will be “hidden” in Qt-Creator (most of them are already hidden).

The total time spent on the second step:

Total time 4 and a half hours. If we take into account the “first stage” , then the total time for creating the application is almost 7 hours. One working day.
It took me about 16 hours of pure time to write this article (almost a week of “dirty”). So it is more profitable to write code than articles :-)

The code can be downloaded from here .
A billet directory for creating a .deb package can be downloaded from here .

Source: https://habr.com/ru/post/99182/


All Articles