Canonical Voices

Diogo Matsubara

  • jgrimm did some clean ups to the server blueprint. He asked to keep the action so he can do further clean ups to it next week.
  • smb didn’t get clarification from kernel team about ownership of kerneloops package but they synced the package anyway.
  • matsubara working on open-iscsi dep8 tests to avoid regressions such as bug https://bugs.launchpad.net/ubuntu/+source/open-iscsi/+bug/1553017
  • smb hopes to help lamont fix a bind/dhcp issue
  • Next meeting will be on Tuesday, Mar 22nd at 16:00 UTC in #ubuntu-meeting.

Full agenda and log

Read more
Michael Hall

As most you you know by now, Ubuntu 16.04 will be dropping the old Ubuntu Software Center in favor of the newer Gnome Software as the graphical front-end to both the Ubuntu archives and 3rd party application store.

Gnome Software

Gnome Software provides a lot of the same enhancements over simple package managers that USC did, and it does this using a new metadata format standard called AppStream. While much of the needed AppStream data can be extracted from the existing packages in the archives, sometimes that’s not sufficient, and that’s when we need people to help fill the gaps.

It turns out that the bulk of the missing or incorrect data is caused by the application icons being used by app packages. While most apps already have an icon, it was never strictly enforced before, and the size and format allowed by the desktop specs was more lenient than what’s needed now.  These lower resolution icons might have been fine for a menu item, but they don’t work very well for a nice, beautiful App Store interface like Gnome Software. And that’s where you can help!

Don’t worry, contributing icons isn’t hard, and it doesn’t require any knowledge of programming or packing to do. Best of all, you’ll not only be helping Ubuntu, but you’ll also be contributing to any other distro that uses the AppStream standard too! In the steps below I will walk you through the process of finding an app in need, getting the correct icon for it, and contributing it to the upstream project and Ubuntu.

1) Pick an App

Because the AppStream data is being automatically extracted from the contents of existing packages, we are able to tell which apps are in need of new icons, and we’ve generated a list of them, sorted by popularity (based on PopCon stats) so you can prioritize your contributions to where they will help the most users. To start working on one, first click the “Create” link to file a new bug report against the package in Ubuntu. Then replace that link in the wiki with a link to your new bug, and put your name in the “Claimed” column so that others know you’ve already started work on it.

Apps with Icon ErrorsNote that a package can contain multiple .desktop files, each of which has it’s own icon, and your bug report will be specific to just that one metadata file. You will also need to be a member of the ~ubuntu-etherpad team (or sub-team like ~ubuntumembers) in order to edit the wiki, you will be asked to verify that membership as part of the login process with Ubuntu SSO.

2) Verify that an AppStream icon is needed

While the extraction process is capable of identifying what packages have a missing or unsupported image in them, it’s not always smart enough to know which packages should have this AppStream data in the first place. So before you get started working on icons, it’s best to first make sure that the metadata file you picked should be part of the AppStream index in the first place.

Because AppStream was designed to be application-centric, the metadata extraction process only looks at those with Type=Application in their .desktop file. It will also ignore any .desktop files with NoDisplay=True in them. If you find a file in the list that shouldn’t be indexed by AppStream, chances are one or both of these values are set incorrectly. In that case you should change your bug description to state that, rather than attaching an icon to it.

3) Contact Upstream

Since there is nothing Ubuntu-specific about AppStream data or icons, you really should be sending your contribution upstream to the originating project. Not only is this best for Ubuntu (carrying patches wastes resources), but it’s just the right thing to do in the open source community. So the after you’ve chosen an app to work on and verfied that it does in fact need a new icon for AppStream, the very next thing you should do is start talking to the upstream project developers.

Start by letting them know that you want to contribute to their project so that it integrates better with AppStream enabled stores (you can reference these Guidelines if they’re not familiar with it), and opening a similar bug report in their bug tracker if they don’t have one already. Finally, be sure to include a link to that upstream bug report in the Ubuntu bug you opened previously so that the Ubuntu developers know the work is also going into upstream to (your contribute might be rejected otherwise).

4) Find or Create an Icon

Chances are the upstream developers already have an icon that meets the AppStream requirements, so ask them about it before trying to find one on your own. If not, look for existing artwork assets that can be used as a logo, and remember that it needs to be at least 64×64 pixels (this is where SVGs are ideal, as they can be exported to any size). Whatever you use, make sure that it matches the application’s current branding, we’re not out to create a new logo for them after all. If you do create a new image file, you will need to make it available under the CC-BY-SA license.

While AppStream only requires a 64×64 pixel image, many desktops (including Unity) will benefit from having even higher resolution icons, and it’s always easier to scale them down than up. So if you have the option, try to provide a 256×256 icon image (or again, just an SVG).

5) Submit your icon

Now that you’ve found (or created) an appropriate icon, it’s time to get it into both the upstream project and Ubuntu. Because each upstream will be different in how they want you to do that, you will need to ask them for guidance (and possibly assistance) in order to do that. Just make sure that you update the upstream bug report with your work, so that the Ubuntu developers can see that it’s been done.

Ubuntu 16.04 has already synced with Debian, so it’s too late for these changes in the upstream project to make their way into this release. In order to get them into 16.04, the Ubuntu packages will have to carry a patch until the changes that land in upstream have the time to make their way into the Ubuntu archives. That’s why it’s so important to get your contribution accepted into the upstream project first, the Ubuntu developers want to know that the patches to their packages will eventually be replaced by the same change from upstream.

attach_file_to_bugTo submit your image to Ubuntu, all you need to do is attach the image file to the bug report you created way back in step #1.

launchpad-subscribeThen, subscribe the “ubuntu-sponsors” team to the bug, these are the Ubuntu developers who will review and apply your icon to the target package, and get it into the Ubuntu archives.

6) Talk about it!

Congratulations, you’ve just made a contribution that is likely to affect millions of people and benefit the entire open source community! That’s something to celebrate, so take to Twitter, Google+, Facebook or your own blog and talk about it. Not only is it good to see people doing these kinds of contributions, it’s also highly motivating to others who might not otherwise get involved. So share your experience, help others who want to do the same, and if you enjoyed it feel free to grab another app from the list and do it again.

Read more
Nicholas Skaggs

Reflections

The joys of Spring (or Fall for our friends in the Southern Hemisphere) are now upon us. The change of seasons spurs us to implement our own changes, to start anew. It's a time to reflect on the past, appreciate it, and then do a little Spring cleaning.

As I write this post to you, I'm doing my own reflecting. It's been quite a journey we've undertaken within the QA community. It's not always been easy, but I think we are poised for even greater success with Xenial than Trusty and Precise LTS's. We have continued ramping up our quality efforts to test new platforms, such as the phone and IOT devices, while also implementing automated testing via things like autopkgtest and autopilot. Nevertheless, the desktop images have continued to release like clockwork. We're testing more things, more often, while still managing to raise our quality bar.

I want to thank all of the volunteers who've helped make each of those releases a reality. Oftentimes quality can be a background job, with thank you's going unsaid, while complaints are easy to find. Truly, it's been wonderful learning and hacking on quality efforts with you. So thank you!

So if this post sounds a bit like a farewell, that's because it is. At least in a way. Moving forward, I'll be transitioning to working on a new challenge. Don't worry, I'm keeping my QA hat on, and staying firmly within the realm of ubuntu! However, the time has come to try my hand at a different side of ubuntu. That's right, it's time to head to the last frontier, juju!

I'll be working on improving the quality story for juju, but I believe juju has real opportunities to enable the testing story within ubuntu too. I'm looking forward to the new challenges, and sharing best practices. We're all working on ubuntu at it's heart, no matter our focus.

Moving forward, I'll still be around in my usual haunts. You'll still be able to poke me on IRC, or send me a mail, and I'm certainly still going to be watching what happens within quality with interest. That said, you are much more likely to find me discussing juju, servers and charms in #juju.

As with anything, please feel free to contact me directly if you have any concerns or questions. I plan to wind down my involvement during the next few weeks. I'll be handing off any lingering project roles, and stepping down gracefully. Ubuntu 'Y' will begin anew, with fresh challenges and fresh opportunities. I know there are folks waiting to tackle them!

Read more
Stéphane Graber

This is the first blog post in this series about LXD 2.0.

LXD logo

A few common questions about LXD

What’s LXD?

At its simplest, LXD is a daemon which provides a REST API to drive LXC containers.

Its main goal is to provide a user experience that’s similar to that of virtual machines but using Linux containers rather than hardware virtualization.

 

How does LXD relate to Docker/Rkt?

This is by far the question we get the most, so lets address it immediately!

LXD focuses on system containers, also called infrastructure containers. That is, a LXD container runs a full Linux system, exactly as it would be when run on metal or in a VM.

Those containers will typically be long running and based on a clean distribution image. Traditional configuration management tools and deployment tools can be used with LXD containers exactly as you would use them for a VM, cloud instance or physical machine.

In contrast, Docker focuses on ephemeral, stateless, minimal containers that won’t typically get upgraded or re-configured but instead just be replaced entirely. That makes Docker and similar projects much closer to a software distribution mechanism than a machine management tool.

The two models aren’t mutually exclusive either. You can absolutely use LXD to provide full Linux systems to your users who can then install Docker inside their LXD container to run the software they want.

Why LXD?

We’ve been working on LXC for a number of years now. LXC is great at what it does, that is, it provides a very good set of low-level tools and a library to create and manage containers.

However that kind of low-level tools aren’t necessarily very user friendly. They require a lot of initial knowledge to understand what they do and how they work. Keeping backward compatibility with older containers and deployment methods has also prevented LXC from using some security features by default, leading to more manual configuration for users.

We see LXD as the opportunity to address those shortcomings. On top of being a long running daemon which lets us address a lot of the LXC limitations like dynamic resource restrictions, container migration and efficient live migration, it also gave us the opportunity to come up with a new default experience, that’s safe by default and much more user focused.

The main LXD components

There are a number of main components that make LXD, those are typically visible in the LXD directory structure, in its command line client and in the API structure itself.

Containers

Containers in LXD are made of:

  • A filesystem (rootfs)
  • A list of configuration options, including resource limits, environment, security options and more
  • A bunch of devices like disks, character/block unix devices and network interfaces
  • A set of profiles the container inherits configuration from (see below)
  • Some properties (container architecture, ephemeral or persistent and the name)
  • Some runtime state (when using CRIU for checkpoint/restore)

Snapshots

Container snapshots are identical to containers except for the fact that they are immutable, they can be renamed, destroyed or restored but cannot be modified in any way.

It is worth noting that because we allow storing the container runtime state, this effectively gives us the concept of “stateful” snapshots. That is, the ability to rollback the container including its cpu and memory state at the time of the snapshot.

Images

LXD is image based, all LXD containers come from an image. Images are typically clean Linux distribution images similar to what you would use for a virtual machine or cloud instance.

It is possible to “publish” a container, making an image from it which can then be used by the local or remote LXD hosts.

Images are uniquely identified by their sha256 hash and can be referenced by using their full or partial hash. Because typing long hashes isn’t particularly user friendly, images can also have any number of properties applied to them, allowing for an easy search through the image store. Aliases can also be set as a one to one mapping between a unique user friendly string and an image hash.

LXD comes pre-configured with three remote image servers (see remotes below):

  • “ubuntu:” provides stable Ubuntu images
  • “ubunt-daily:” provides daily builds of Ubuntu
  • “images:” is a community run image server providing images for a number of other Linux distributions using the upstream LXC templates

Remote images are automatically cached by the LXD daemon and kept for a number of days (10 by default) since they were last used before getting expired.

Additionally LXD also automatically updates remote images (unless told otherwise) so that the freshest version of the image is always available locally.

Profiles

Profiles are a way to define container configuration and container devices in one place and then have it apply to any number of containers.

A container can have multiple profiles applied to it. When building the final container configuration (known as expanded configuration), the profiles will be applied in the order they were defined in, overriding each other when the same configuration key or device is found. Then the local container configuration is applied on top of that, overriding anything that came from a profile.

LXD ships with two pre-configured profiles:

  • “default” is automatically applied to all containers unless an alternative list of profiles is provided by the user. This profile currently does just one thing, define a “eth0” network device for the container.
  • “docker” is a profile you can apply to a container which you want to allow to run Docker containers. It requests LXD load some required kernel modules, turns on container nesting and sets up a few device entries.

Remotes

As I mentioned earlier, LXD is a networked daemon. The command line client that comes with it can therefore talk to multiple remote LXD servers as well as image servers.

By default, our command line client comes with the following remotes defined

  • local: (default remote, talks to the local LXD daemon over a unix socket)
  • ubuntu: (Ubuntu image server providing stable builds)
  • ubuntu-daily: (Ubuntu image server providing daily builds)
  • images: (images.linuxcontainers.org image server)

Any combination of those remotes can be used with the command line client.

You can also add any number of remote LXD hosts that were configured to listen to the network. Either anonymously if they are a public image server or after going through authentication when managing remote containers.

It’s that remote mechanism that makes it possible to interact with remote image servers as well as copy or move containers between hosts.

Security

One aspect that was core to our design of LXD was to make it as safe as possible while allowing modern Linux distributions to run inside it unmodified.

The main security features used by LXD through its use of the LXC library are:

  • Kernel namespaces. Especially the user namespace as a way to keep everything the container does separate from the rest of the system. LXD uses the user namespace by default (contrary to LXC) and allows for the user to turn it off on a per-container basis (marking the container “privileged”) when absolutely needed.
  • Seccomp. To filter some potentially dangerous system calls.
  • AppArmor: To provide additional restrictions on mounts, socket, ptrace and file access. Specifically restricting cross-container communication.
  • Capabilities. To prevent the container from loading kernel modules, altering the host system time, …
  • CGroups. To restrict resource usage and prevent DoS attacks against the host.

Rather than exposing those features directly to the user as LXC would, we’ve built a new configuration language which abstracts most of those into something that’s more user friendly. For example, one can tell LXD to pass any host device into the container without having to also lookup its major/minor numbers to manually update the cgroup policy.

Communications with LXD itself are secured using TLS 1.2 with a very limited set of allowed ciphers. When dealing with hosts outside of the system certificate authority, LXD will prompt the user to validate the remote fingerprint (SSH style), then cache the certificate for future use.

The REST API

Everything that LXD does is done over its REST API. There is no other communication channel between the client and the daemon.

The REST API can be access over a local unix socket, only requiring group membership for authentication or over a HTTPs socket using a client certificate for authentication.

The structure of the REST API matches the different components described above and is meant to be very simple and intuitive to use.

When a more complex communication mechanism is required, LXD will negotiate websockets and use those for the rest of the communication. This is used for interactive console session, container migration and for event notification.

With LXD 2.0, comes the /1.0 stable API. We will not break backward compatibility within the /1.0 API endpoint however we may add extra features to it, which we’ll signal by declaring additional API extensions that the client can look for.

Containers at scale

While LXD provides a good command line client, that client isn’t meant to manage thousands of containers on multiple hosts. For that kind of use cases, we have nova-lxd which is an OpenStack plugin that makes OpenStack treat LXD containers in the exact same way it would treat VMs.

This allows for very large deployments of LXDs on a large number of hosts, using the OpenStack APIs to manage network, storage and load-balancing.

Extra information

The main LXD website is at: https://linuxcontainers.org/lxd
Development happens on Github at: https://github.com/lxc/lxd
Mailing-list support happens on: https://lists.linuxcontainers.org
IRC support happens in: #lxcontainers on irc.freenode.net

And if you can’t wait until the next few posts to try LXD, you can take our guided tour online and try it for free right from your web browser!

Read more
Stéphane Graber

As we are getting closer and closer to tagging the final releases of LXC, LXD and LXCFS 2.0, I figured it would be a good idea to talk a bit about everything that went into LXD since we first started that project a year and a half ago.

LXD logo

This is going to be a blog post series similar to what I’ve done for LXC 1.0 a couple years back.

The topics that will be covered are:

I’m hoping to post a couple of those every week for the coming month and a half leading to the Ubuntu 16.04 release.

If you can’t wait for all of those to come out to play with LXD, you can also take the guided tour and play with LXD, online through our online demo page.

Read more
Colin Ian King

A frequently used incorrect realloc() idiom

While running static analysis on a lot of C source code, I keep on finding a common incorrect programming idiom used with realloc() allocation failures where a NULL is returned and the code returns with some kind of exit failure status, something like the following:

 ptr = realloc(ptr, new_size);  
if (!ptr)
return -ENOMEM; /* Failed, no memory! */

However, when realloc() fails it returns NULL and the original object remains unchanged and thus it is not freed.  So the above code leaks the memory pointed to by ptr if realloc() returns NULL.

This may be a moot point, since the error handling paths normally abort the program because we are out of memory if can't proceed any further.  However, there are occasions in code where ENOMEM may not be fatal, for example the program may reallocate smaller buffers and retry or free up space on the heap and retry.

A more correct programming idiom for realloc() perhaps should be:

 tmp = realloc(ptr, new_size);  
if (!tmp) {
free(ptr);
return -ENOMEM; /* Failed, no memory! */
}
ptr = tmp;

..which is not aesthetically pleasing, but does the trick of free'ing memory before we return.

Anyhow, it is something to bear in mind next time one uses realloc().

Read more
Anthony Dillon

The Juju web resources are made up of two entities: a website jujucharms.com and an app called Juju GUI, which can be demoed at demo.jujucharms.com.

Applying Vanilla to jujucharms.com

Luckily the website was already using our old style guidelines, which we refactored and improved to become Vanilla, so I removed the guidelines link from the head and the site fell to pieces. Once I NPM installed vanilla-framework and included it into the main sass file things started to look up.

A few areas of the site needed to be updated, like moving the search markup outside of the nav element. This is due to header improvements in the transition from guidelines to Vanilla. Also we renamed and BEMed our inline-list component, so I updated its markup in the process. The mobile navigation was also replaced with the new non-JavaScript version from Vanilla.

To my relief with these minor changes the site looked almost exactly as it did before. There were some padding differences, which resulted in some larger spacing between rows, but this was a purposeful update.

All in all the process of replacing guidelines with Vanilla on the website was quick and easy.

Now into the unknown…

Applying Vanilla to Juju GUI

I expected this step to be trickier as the GUI had not started life using guidelines and was using entirely bespoke CSS. So I thought: let’s install it, link the Vanilla framework and see how it looks.

To my surprise the app stayed together, apart from some element movement and overriding of input styling. We didn’t need the entire framework to be included so I selectively included only the core modules like typography, grid, etc.

The only major difference is that Vanilla applies bottom margin to lists, which did not exist on the app before, so I applied “margin-bottom: 0” to each list component as a local override.

Once I completed these changes it looked exactly as before.

What’s the benefit

You might be thinking, as I did at the beginning of the project, “that is a lot of work to have both projects look exactly the same”, when in fact it brings a number of benefits.

Now we have consistent styling across the Juju real estates, which are tied together with one single base CSS framework. This means we have exactly the same grid, buttons, typography, padding and much more. The tech debt to keep these in sync has been cut and allows designers to work from a single component list.

Future

We’re not finished there, as Vanilla framework is a bare bones CSS framework it also has a concept of theming. The next step will be to refactor the SCSS on both projects and identify the common components. The theme itself depends on Vanilla, so we have logical layering.

In the end

It is exciting to see how versatile Vanilla is. Whether it’s a web app or web site, Vanilla helps us keep our styles consistent. The layered inheritance gives us the flexibility to selectively include modules and extend them when required.

Read more
Rae Shambrook

We previously posted about the clock app’s new look and today we are getting to know one of the developers behind the clock ( as well as other community apps.)  Bartosz Kosiorek gives us a glimpse into developing for Ubuntu and how he got started.

1) First, can you give us a bit of background about yourself and tell us how you started developing for Ubuntu?

My name is Bartosz and I’m from Poland. Currently I’m the developer for the Ubuntu Clock and Ubuntu Calculator. I started contributing to Ubuntu in 2008, by submitting bug reports into launchpad and fixing translations. Later I participated in the One Hundred Papercuts project. I made SRU verifications and eventually started developing.

My adventure with Ubuntu started from Ubuntu 8.10 (Interpid Idex). Previously I tried many different distributions (Debian, Fedora, SuSE etc.). I chose Ubuntu because it is easy to use and after installation, I have fully functional system. I like that after Ubuntu is installed, there are no duplicate applications and those already installed work perfectly with the system.

2) How long have you been working on the Clock and Calculator? How did you get involved in these projects?

I started to develop for Ubuntu about two years ago when I first heard about Ubuntu Touch and convergence. I started by contributing to Ubuntu Core Apps by testing, submitting bug reports and patches. Most commits were done for Ubuntu Calculator and Ubuntu Clock by fixing bugs which were approved by Riccardo Padovani, Mihir Soni and Nekhelesh Ramananthan. After some time, I became member of Ubuntu Core Apps. It’s very fun to work with these guys and the Ubuntu community. I’ve learned a lot about Qt/QML and user experience design.

3) How do you approach implementing a design in your apps?

Generally I follow the design document during implementation and sometimes find parts that need to be improved. After speaking with the Ubuntu UX team, we discuss various issues and agree on a final design solution. Sometimes the UX team gives us freehand, so we could design some parts by ourselves (eg. Stopwatch, Welcome Wizard, Landscape View). It’s really fun to work with such awesome guys.

4) What feature would you like to see in the future?

I think from user perspective, longer battery life is a very important topic. The power usage is higher with white background: https://www.quora.com/Does-a-white-background-use-more-energy-on-a-LCD-than-if-it-was-set-to-black especially with OLED screen. I wish that Ubuntu Touch came with a darker theme, to save battery on OLED screens.

Read more
Dustin Kirkland


We at Canonical have conducted a legal review, including discussion with the industry's leading software freedom legal counsel, of the licenses that apply to the Linux kernel and to ZFS.

And in doing so, we have concluded that we are acting within the rights granted and in compliance with their terms of both of those licenses.  Others have independently achieved the same conclusion.  Differing opinions exist, but please bear in mind that these are opinions.

While the CDDL and GPLv2 are both "copyleft" licenses, they have different scope.  The CDDL applies to all files under the CDDL, while the GPLv2 applies to derivative works.

The CDDL cannot apply to the Linux kernel because zfs.ko is a self-contained file system module -- the kernel itself is quite obviously not a derivative work of this new file system.

And zfs.ko, as a self-contained file system module, is clearly not a derivative work of the Linux kernel but rather quite obviously a derivative work of OpenZFS and OpenSolaris.  Equivalent exceptions have existed for many years, for various other stand alone, self-contained, non-GPL kernel modules.

Our conclusion is good for Ubuntu users, good for Linux, and good for all of free and open source software.

As we have already reached the conclusion, we are not interested in debating license compatibility, but of course welcome the opportunity to discuss the technology.

Cheers,
Dustin

EDIT: This post was updated to link to the supportive position paper from Eben Moglen of the SFLC, an amicus brief from James Bottomley, as well as the contrarian position from Bradley Kuhn and the SFC.

Read more
UbuntuTouch

在Ubuntu.Components 1.3上推出了一个新的PageHeader.它是用来代替以前版本Ubuntu.Component 1.1/1.2中的Page.title及Page.head.在编程的时候,如果PageHeader存在的话,那么Page.title及Page.head将不在起任何的作用.本文章的英文出处为"PageHeader tutorial".英文好的开发者可以直接读英文的文章.在Ubuntu.Components 1.3中,每个页面可以有自己单独的Page.header,而且每个header都可以是任何一个Item.另外值得指出的是Page.header的父亲是每个页面的Page.


1)新的Page header属性


为了说明问题,我们先来做一个简单的应用:

Main.qml


import QtQuick 2.4
import Ubuntu.Components 1.3

/*!
    \brief MainView with a Label and Button elements.
*/

MainView {
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "pageheader1.liu-xiao-guo"

    width: units.gu(60)
    height: units.gu(85)

    Page {
        id: page
        header: Image {
            height: units.gu(8)
            width: parent.width
            source: "images/image1.jpg"
        }

        Image {
            anchors {
                left: parent.left
                right: parent.right
                bottom: parent.bottom
                top: page.header.bottom
            }
            source: "images/image2.jpg"
        }
    }
}

在上面的代码中,我们可以看出来header的父亲是Page,并且它的高度是我们可以自己定义的.事实上它可以是任何一个我们可以定义的Item.我们可以定义我们任何喜欢的Component上去,而不是先前我们只能定义一个Page.title.运行我们的代码:





2)使用PageHeader作为一个page的header


在上面的练习中我们可以随意用一个Item来当做我们的header,但是对于有些应用,我们还是希望有像我们以前Page.title那样的特性来使得我们的每个页面有自己的title等.我们可以使用PageHeader完成我们所需要的功能.同时PageHeader也可以让我们在header上放上我们所需要的action.我们还是来通过一个例程来展示它的功能:

Main.qml


import QtQuick 2.4
import Ubuntu.Components 1.3

/*!
    \brief MainView with a Label and Button elements.
*/

MainView {
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "pageheader2.liu-xiao-guo"

    width: units.gu(60)
    height: units.gu(85)

    Page {
        header: PageHeader {
            title: "Ubuntu header"
            leadingActionBar.actions: [
                Action {
                    iconName: "contact"
                    text: "Navigation 1"
                },
                Action {
                    iconName: "calendar"
                    text: "Navigation 2"
                },
                Action {
                    iconName: "contact"
                    text: "Navigation 1"
                },
                Action {
                    iconName: "calendar"
                    text: "Navigation 2"
                }
            ]
            trailingActionBar.actions: [
                Action {
                    iconName: "settings"
                    text: "First"
                },
                Action {
                    iconName: "info"
                    text: "Second"
                },
                Action {
                    iconName: "search"
                    text: "Third"
                },
                Action {
                    iconName: "settings"
                    text: "First"
                },
                Action {
                    iconName: "info"
                    text: "Second"
                },
                Action {
                    iconName: "search"
                    text: "Third"
                }
            ]
        }
    }
}

运行我们的代码:


  

从上面我们可以看出来,我们可以在header上面加上leadingActionBar(如果被定义,在PageStack及AdaptivePageLayout被使用时,""按钮将被自动影藏)及trailingActionBar(如果数量多,就会出现"三"字行的符号供打开).



3)自动显示及影藏header



上面的两个例程都是显示了如何静态地显示一个Page.header,但是在我们的实际应用中,我们有时希望能够动态地影藏或显示我们的header,比如在一个ListView被向上滚动时,我们希望header被自动影藏以显示更多的区间给我们的应用.

Main.qml


import QtQuick 2.4
import Ubuntu.Components 1.3

/*!
    \brief MainView with a Label and Button elements.
*/

MainView {
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "pageheader3.liu-xiao-guo"

    width: units.gu(60)
    height: units.gu(85)

    Page {
        header: PageHeader {
            title: "Ubuntu header"
            flickable: listView
            trailingActionBar.actions: [
                Action {
                    iconName: "info"
                    text: "Information"
                }
            ]
        }
        ListView {
            id: listView
            anchors.fill: parent
            model: 20
            delegate: ListItem {
                Label {
                    anchors.centerIn: parent
                    text: "Item " + index
                }
            }
        }
    }

}

在上面的代码中,我们把header的flickable属性定义为listView.这样当我们向上滑动我们的列表时,header就会被自动影藏.

  



4)扩展header


我们可以使用header的extension属性来扩展我们的header.这个extension的属性可以是任何一个QML Item.它存在于header的正下方.我们可以利用header中的contents属性来替换掉默认的title属性.我们还是先来看一个例程:

import QtQuick 2.4
import Ubuntu.Components 1.3

/*!
    \brief MainView with a Label and Button elements.
*/

MainView {
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "pageheader4.liu-xiao-guo"

    width: units.gu(60)
    height: units.gu(85)

    Page {
        id: page
        header: standardHeader
        Label {
            anchors {
                horizontalCenter: parent.horizontalCenter
                top: page.header.bottom
                topMargin: units.gu(5)
            }
            text: "Use the icons in the header."
            visible: standardHeader.visible
        }
        PageHeader {
            id: standardHeader
            visible: page.header === standardHeader
            title: "Default title"
            trailingActionBar.actions: [
                Action {
                    iconName: "search"
                    text: "Search"
                    onTriggered: page.header = searchHeader
                },
                Action {
                    iconName: "edit"
                    text: "Edit"
                    onTriggered: page.header = editHeader
                }
            ]
        }
        PageHeader {
            id: searchHeader
            visible: page.header === searchHeader
            leadingActionBar.actions: [
                Action {
                    iconName: "back"
                    text: "Back"
                    onTriggered: page.header = standardHeader
                }
            ]
            contents: TextField {
                anchors {
                    left: parent.left
                    right: parent.right
                    verticalCenter: parent.verticalCenter
                }
                placeholderText: "Search..."
            }
        }
        PageHeader {
            id: editHeader
            visible: page.header === editHeader
            property Component delegate: Component {
                AbstractButton {
                    id: button
                    action: modelData
                    width: label.width + units.gu(4)
                    height: parent.height
                    Rectangle {
                        color: UbuntuColors.blue
                        opacity: 0.1
                        anchors.fill: parent
                        visible: button.pressed
                    }
                    Label {
                        anchors.centerIn: parent
                        id: label
                        text: action.text
                        font.weight: text === "Confirm"
                                     ? Font.Normal
                                     : Font.Light
                    }
                }
            }
            leadingActionBar {
                anchors.leftMargin: 0
                actions: Action {
                    text: "Cancel"
                    iconName: "close"
                    onTriggered: page.header = standardHeader
                }
                delegate: editHeader.delegate
            }
            trailingActionBar {
                anchors.rightMargin: 0
                actions: Action {
                    text: "Confirm"
                    iconName: "tick"
                    onTriggered: page.header = standardHeader
                }
                delegate: editHeader.delegate
            }
            extension: Toolbar {
                anchors {
                    left: parent.left
                    right: parent.right
                    bottom: parent.bottom
                }
                trailingActionBar.actions: [
                    Action { iconName: "bookmark-new" },
                    Action { iconName: "add" },
                    Action { iconName: "edit-select-all" },
                    Action { iconName: "edit-copy" },
                    Action { iconName: "select" }
                ]
                leadingActionBar.actions: Action {
                    iconName: "delete"
                    text: "delete"
                    onTriggered: print("Delete action triggered")
                }
            }
        }
    }
}

运行我们的例程:

    



5)定制header


我们可以对header的显示进行定制,这样可以根据我们的喜好进行颜色的搭配等.我们可以使用StyleHints来修改我们的显示.大家也可以设计自己的主题并通过PageHeaderStyle来调整我们的显示.

Main.qml


import QtQuick 2.4
import Ubuntu.Components 1.3

/*!
    \brief MainView with a Label and Button elements.
*/

MainView {
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "pageheader5.liu-xiao-guo"

    width: units.gu(100)
    height: units.gu(75)

    Page {
        header: PageHeader {
            title: "Ubuntu header"
            StyleHints {
                foregroundColor: "white"
                backgroundColor: UbuntuColors.blue
                dividerColor: UbuntuColors.ash
                contentHeight: units.gu(7)
            }
            trailingActionBar {
                actions: [
                    Action {
                        iconName: "info"
                        text: "Information"
                    },
                    Action {
                        iconName: "settings"
                        text: "Settings"
                    }
                ]
            }
        }
    }
}

运行我们的应用:



我们发现我们的header的颜色发生了改变.项目的源码在:https://github.com/liu-xiao-guo/pageheader5


作者:UbuntuTouch 发表于2016/3/9 17:52:21 原文链接
阅读:139 评论:0 查看评论

Read more
UbuntuTouch

粒子是计算机图形技术,可视化某些图形效果。典型的影响可能是:落叶,火灾,爆炸,流星,云,等。

它不同于其他的图形渲染原因粒子渲染是基于模糊的方面。结果是不能在像素基底准确预测的。参数到粒子系统描述为随机模拟的边界。与粒子渲染的现象,往往很难与传统的渲染技术,可视化。好处是可以让要素QML与粒子系统交互。可以使用传统的动画技术动画通过属性的控制来实现动画效果。


概念


在粒子模拟的心脏是控制共享时间线的ParticleSystem。一个场景可以有几个粒子系统,他们每个人具有独立的时间线。一个粒子是使用Emitter发射并用ParticlePainter来呈现,它可以是图像,QML项。发射器也提供了用于控制粒子向量空间的方向。一旦粒子被发射,它将不再被发射器所控制。粒子模块提供Affector,它用来操控粒子被发射后的行为。
在一个系统中的颗粒可以共享使用ParticleGroup元件定时切换。默认情况下,每一个粒子都是属于是空的('')组。


  • ParticleSystem - manages shared time-line between emitters
  • Emitter - emits logical particles into the system
  • ParticlePainter - particles are visualized by a particle painter
  • Direction - vector space for emitted particles
  • ParticleGroup - every particle is a member of a group
  • Affector - manipulates particles after they have been emitted
一个粒子系统基本是由ParticleSystem、ImageParticle(ParticlePainter)以及Emitter组成的。其中ParticleSystem必不可少,因为这是要控制好各个粒子系统组件的必备类型。如果ParticleSystem是不作为Emitter的父类存在的话,那么Emitter有一个成员system必须要指定ParticleSystem的id。Emitter也是一个必不可少的类,它的作用是规定这些例子以何种方式发射、以及规定粒子的大小和生命周期。而ImageParticle是ParticlePainter的子类,它不是必备的,我们可以采用ParticlePainter的其它子类CustomParticle和ItemParticle来指定。它的作用是规定粒子的图片以及旋转、颜色、透明度等信息。

其实在三者之外,还有一个不可忽视的类,那就是Affector。一般来说,粒子在Emitter作用后会保持初始的速度、加速度和大小进行运动,此后这些数值不再受Emitter控制了,只有Affector才能控制粒子在运行过程中的数值大小。这里Affector只是一个基类,在它的基础上定义出来了很多根据不同效果而定义的子类。比如说Age、Attractor、Friction、Gravity、GroupGoal、SpriteGoal、Turbulence和Wander。

我们先来做一个简单的例子:

Main.qml


import QtQuick 2.0
import QtQuick.Particles 2.0
import Ubuntu.Components 1.1


MainView {
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "particle1.liu-xiao-guo"

    width: units.gu(100)
    height: units.gu(75)

    Page {
        title: i18n.tr("particle1")

        ParticleSystem {
            id: particle
            anchors.fill: parent
            running: true

            ImageParticle {
                anchors.fill: parent
//                source: "qrc:///particleresources/star.png"
                source: "images/starfish_1.png"
                alpha: 0.5
                alphaVariation: 0.2
                colorVariation: 1.0
            }

            Emitter {
                anchors.centerIn: parent
                emitRate: 400
                lifeSpan: 5000
                size: 20
                sizeVariation: 8
                velocity: AngleDirection {angleVariation: 180; magnitude: 60}
            }

            Turbulence {
                anchors.fill: parent
                strength: 2
            }
        }
    }
}

在上面的例子中,我们使用了一个ImageParticle.它继承于ParticlePainter.用来显示每个粒子.我们同时也定义了一个发射器.在里面我们可以定义每个粒子的生命期,大小,方向及大小.运行我们的应用:



我们可以修改上面的例程的一些参数,比如我们修改AngleDirection里的角度就图片.



上面的例程的源码在https://github.com/liu-xiao-guo/particle1. 我们上面的代码也可以使用如下的格式:

    Page {
        title: i18n.tr("particle3")

        ParticleSystem {
            id: particleSystem
        }

        Emitter {
            id: emitter
            anchors.centerIn: parent
            anchors.fill: parent
            system: particleSystem
            emitRate: 10
            lifeSpan: 2000
            lifeSpanVariation: 500
            size: 54
            endSize: 32
        }

        ImageParticle {
            source: "images/realLeaf1.png"
            system: particleSystem
        }
    }

这里Emitter不再被ParticleSystem所包含,但是我们必须在里面定义一个叫做system的属性.

我们修改我们上面的例子.我们使用Gravity Affector. 在Gravity中,我们可以使用加速度及角度.整个例程的代码为:


import QtQuick 2.0
import Ubuntu.Components 1.1
import QtQuick.Particles 2.0

/*!
    \brief MainView with a Label and Button elements.
*/

MainView {
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "particle2.liu-xiao-guo"

    width: units.gu(100)
    height: units.gu(75)

    Page {
        title: i18n.tr("particle2")

        ParticleSystem
        {
            anchors.centerIn: parent
            running: true

            ImageParticle {
                anchors.fill: parent
                // source: "qrc:///particleresources/star.png"
                source: "images/starfish_0.png"
                alpha: 0.5
                alphaVariation: 0.2
                colorVariation: 1.0
            }

            Emitter
            {
                emitRate: 20
                size: 50
                lifeSpan: 5000
                velocity: AngleDirection { magnitude: 100; angleVariation: 360  }
            }

            Gravity
            {
                angle: 90
                magnitude: 100
            }

            Turbulence {
                anchors.fill: parent
                strength: 2
            }
        }
    }
}

运行我们的例程,效果如下:


如上图所示,我们可以看到一种重力的效果.整个项目的源码在 https://github.com/liu-xiao-guo/particle2


作者:UbuntuTouch 发表于2016/2/2 11:08:08 原文链接
阅读:355 评论:0 查看评论

Read more
UbuntuTouch

我刚发现有一本关于BQ Aquaris E4.5 Ubuntu phone的书,里面有介绍如何使用BQ Ubuntu手机的.我想对大多数的Ubuntu手机黑客是非常有帮助的.里面的很多的命令及细节对如何使用Ubuntu手机是非常有帮助的.其实大多数的内容对其它的Ubuntu手机也是有帮助的.书的链接地址在:


https://gurucubano.gitbooks.io/bq-aquaris-e-4-5-ubuntu-phone/content/en/index.html


如果谁有把这本书翻译完成的,请一定告诉我.然后我在让广大的开发者们谢谢你!:)

作者:UbuntuTouch 发表于2016/2/29 7:47:22 原文链接
阅读:278 评论:0 查看评论

Read more
UbuntuTouch

[原]在QML应用中显示image tag

我们在许多的手机中应用中可以看见在文字中可以插入图片.这对于我们的有些应用添加很多的情趣.在今天的例程中,我们通过一个很小的例程来展示如何实现同样的功能.


   


要实现上面的功能其实并不难.我们首先来看一下QML中的Text的用法.

text : string
The text to display. Text supports both plain and rich text strings.

从上面的描述中,我们可以看出Text是支持plain及rich text的.也就是说,它支持像HTML格式的文本输出.基于这样的特性,我们可以设计我们的程序如下:

TextWithImage.qml


import QtQuick 2.0

Text {
    width: parent.width
    font.pointSize: 30
    wrapMode: Text.WordWrap
    textFormat: Text.StyledText
    horizontalAlignment: main.hAlign
}

Main.qml

import QtQuick 2.0
import Ubuntu.Components 1.1

/*!
    \brief MainView with a Label and Button elements.
*/

MainView {
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "imagetag.liu-xiao-guo"

    /*
     This property enables the application to change orientation
     when the device is rotated. The default is false.
    */
    //automaticOrientation: true

    // Removes the old toolbar and enables new features of the new header.
    useDeprecatedToolbar: false

    width: units.gu(60)
    height: units.gu(85)

    Page {
        id: main
        title: i18n.tr("Image Tags")

        property var hAlign: Text.AlignLeft

        Flickable {
            anchors.fill: parent
            anchors.bottomMargin:buttons.height
            contentWidth: parent.width
            contentHeight: col.height + 20

            Column {
                id: col
                x: 10; y: 10
                spacing: 20
                width: parent.width - 20

                TextWithImage {
                    text: "This is a <b>happy</b> face<img src=\"images/face-smile.png\">"
                }
                TextWithImage {
                    text: "This is a <b>very<img src=\"images/face-smile-big.png\" align=\"middle\"/>happy</b> face vertically aligned in the middle."
                }
                TextWithImage {
                    text: "This is a tiny<img src=\"images/face-smile.png\" width=\"15\" height=\"15\">happy face."
                }
                TextWithImage {
                    text: "This is a<img src=\"images/starfish_2.png\" width=\"50\" height=\"50\" align=\"top\">aligned to the top and a<img src=\"images/heart200.png\" width=\"50\" height=\"50\">aligned to the bottom."
                }
                TextWithImage {
                    text: "Qt logos<img src=\"images/qtlogo.png\" width=\"55\" height=\"60\" align=\"middle\"><img src=\"images/qtlogo.png\" width=\"37\" height=\"40\" align=\"middle\"><img src=\"images/qtlogo.png\" width=\"18\" height=\"20\" align=\"middle\">aligned in the middle with different sizes."
                }
                TextWithImage {
                    text: "Some hearts<img src=\"images/heart200.png\" width=\"20\" height=\"20\" align=\"bottom\"><img src=\"images/heart200.png\" width=\"30\" height=\"30\" align=\"bottom\"> <img src=\"images/heart200.png\" width=\"40\" height=\"40\"><img src=\"images/heart200.png\" width=\"50\" height=\"50\" align=\"bottom\">with different sizes."
                }
                TextWithImage {
                    text: "Resized image<img width=\"48\" height=\"48\" align=\"middle\" src=\"http://qt-project.org/images/qt13a/Qt-logo.png\">from the internet."
                }
                TextWithImage {
                    text: "Image<img align=\"middle\" src=\"http://qt-project.org/images/qt13a/Qt-logo.png\">from the internet."
                }
                TextWithImage {
                    height: 120
                    verticalAlignment: Text.AlignVCenter
                    text: "This is a <b>happy</b> face<img src=\"images/face-smile.png\"> with an explicit height."
                }
            }
        }

        Keys.onUpPressed: main.hAlign = Text.AlignHCenter
        Keys.onLeftPressed: main.hAlign = Text.AlignLeft
        Keys.onRightPressed: main.hAlign = Text.AlignRight

        Row {
            id: buttons
            anchors.bottom: parent.bottom
            anchors.horizontalCenter: parent.horizontalCenter
            spacing: units.gu(2)

            Button {
                text: "Align Left"
                onClicked: {
                    main.hAlign = Text.AlignLeft
                }
            }

            Button {
                text: "Align Center"
                onClicked: {
                    main.hAlign = Text.AlignHCenter
                }
            }

            Button {
                text: "Align Right"
                onClicked: {
                    main.hAlign = Text.AlignRight
                }
            }
        }
    }
}

在上面,我们可以看出来,我们在Text中使用了HTML格式的文本输出.这样就可以生成就像我们在文章开始部分显示的应用输出.

应用所有的源码在:https://github.com/liu-xiao-guo/imagetag





作者:UbuntuTouch 发表于2016/1/14 15:18:16 原文链接
阅读:400 评论:0 查看评论

Read more
UbuntuTouch

在这篇文章中,我们将展示如何在我们的QML应用中使用不同的font.我们既可以使用本地应用带有的字体,也可以使用系统带有的字体.我们也展示了如何使用一个在网路上的字体.


为了使用字体,我们可以通过如下的方式来使用它:


       Text {
            text: myText
            color: "lightsteelblue"
            width: parent.width
            wrapMode: Text.WordWrap
            font.family: "Times"
            font.pixelSize: size
        }

具体的使用说明可以参考连接QML Text.当然,我们也可以使用一种简洁的格式:

       Text {
            text: myText
            color: "lightsteelblue"
            width: parent.width
            wrapMode: Text.WordWrap
            horizontalAlignment: Text.AlignHCenter
            font { family: "Times"; pixelSize: size; capitalization: Font.AllUppercase }
        }


我们可以在上面一行中定义font的所有的属性.

为了使用不同的font,我们可以使用FontLoader来装载我们的font:

    FontLoader { id: fixedFont; name: "Courier" }
    FontLoader { id: localFont; source: "content/fonts/tarzeau_ocr_a.ttf" }
    FontLoader { id: webFont; source: "http://www.princexml.com/fonts/steffmann/Starburst.ttf" }

在上面的代码中,我们使用了本地的Courier字体,我们也使用了在本地文件目录中的字体tarzeau_ocr_a.ttf,同时我们也定义了一个网路的字体.该字体文件存在于网路的一个地址.在实际的使用中,该字体将本载入到我们的应用中使用.

为了说明问题,我们直接把我的源码写出来:

Fonts.qml

import QtQuick 2.0

Rectangle {
    property string myText: "The quick brown fox jumps over the lazy dog."

    width: 320; height: 480
    color: "steelblue"

    FontLoader { id: fixedFont; name: "Courier" }
    FontLoader { id: localFont; source: "content/fonts/tarzeau_ocr_a.ttf" }
    FontLoader { id: webFont; source: "http://www.princexml.com/fonts/steffmann/Starburst.ttf" }
    property int size: 40

    Column {
        anchors { fill: parent; leftMargin: 10; rightMargin: 10; topMargin: 10 }
        spacing: 15

        Text {
            text: myText
            color: "lightsteelblue"
            width: parent.width
            wrapMode: Text.WordWrap
            font.family: "Times"
            font.pixelSize: size
        }
        Text {
            text: myText
            color: "lightsteelblue"
            width: parent.width
            wrapMode: Text.WordWrap
            horizontalAlignment: Text.AlignHCenter
            font { family: "Times"; pixelSize: size; capitalization: Font.AllUppercase }
        }
        Text {
            text: myText
            color: "lightsteelblue"
            width: parent.width
            horizontalAlignment: Text.AlignRight
            wrapMode: Text.WordWrap
            font { family: fixedFont.name; pixelSize: size; weight: Font.Bold; capitalization: Font.AllLowercase }
        }
        Text {
            text: myText
            color: "lightsteelblue"
            width: parent.width
            wrapMode: Text.WordWrap
            font { family: fixedFont.name; pixelSize: size; italic: true; capitalization: Font.SmallCaps }
        }
        Text {
            text: myText
            color: "lightsteelblue"
            width: parent.width
            wrapMode: Text.WordWrap
            font { family: localFont.name; pixelSize: size; capitalization: Font.Capitalize }
        }
        Text {
            text: {
                if (webFont.status == FontLoader.Ready) myText
                else if (webFont.status == FontLoader.Loading) "Loading..."
                else if (webFont.status == FontLoader.Error) "Error loading font"
            }
            color: "lightsteelblue"
            width: parent.width
            wrapMode: Text.WordWrap
            font.family: webFont.name; font.pixelSize: size
        }
    }
}

运行我们的应用,显示的画面为:



我们的源码地址为:https://github.com/liu-xiao-guo/fonts




作者:UbuntuTouch 发表于2016/1/4 8:27:17 原文链接
阅读:479 评论:0 查看评论

Read more
UbuntuTouch

在这篇文章中,我们来显示在Ubuntu 手机中所有的已经有的字体.大家可以根据自己的需求来选择自己所需要的字体.我们已经在先前的文章" 如何在QML中使用不同的字体(font)"已经展示了如何使用font来显示不同的字体.


我们可以通过如下的方式来显示所有的字体:


AvailableFonts.qml


import QtQuick 2.0
import Ubuntu.Components 1.1

Rectangle {
    color: "steelblue"
    property int size: 60

    ListView {
        clip: true
        anchors.fill: parent
        model: Qt.fontFamilies()

        delegate: Item {
            height: units.gu(4)
            width: ListView.view.width

            Row {
                height: parent.height
                width: parent.width

                Text {
                    anchors.verticalCenter: parent.verticalCenter
                    text: "I love you!"
                    width: units.gu(25)
                    font { family: modelData; pixelSize: size }
                }

                Text {
                    anchors.verticalCenter: parent.verticalCenter
                    text: modelData
                    color: "white"
                }
            }

            Rectangle {
                color: "red"
                height: 2
                width: parent.width
            }
        }
    }
}


在我们的Main.qml中,我们直接调用:

Main.qml


import QtQuick 2.0
import Ubuntu.Components 1.1

/*!
    \brief MainView with a Label and Button elements.
*/

MainView {
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "fontlist.liu-xiao-guo"

    /*
     This property enables the application to change orientation
     when the device is rotated. The default is false.
    */
    //automaticOrientation: true

    // Removes the old toolbar and enables new features of the new header.
    useDeprecatedToolbar: false

    width: units.gu(60)
    height: units.gu(85)

    Page {
        title: i18n.tr("Font list")

        Text {
            id: txt
            anchors.top: parent.top
            anchors.horizontalCenter: parent.horizontalCenter
            text: "我爱你 " + font.family + " " + font.pixelSize
            height: units.gu(3)
        }

        AvailableFonts {
            anchors.fill: parent
            anchors.topMargin:txt.height
        }
    }
}


运行我们的应用:




作者:UbuntuTouch 发表于2016/1/4 9:25:26 原文链接
阅读:413 评论:0 查看评论

Read more
UbuntuTouch

[原]snappy ubuntu core 演示

基于对snappy ubuntu core的理解,我做了几个展示的应用.


1)利用snappy ubuntu core来控制piglow


2)利用snappy ubuntu core来收集传感器数据及控制LED灯



3)利用snappy ubuntu core来监控webcam



更多关于snappy ubuntu core的介绍,可以参阅文章"到底Snappy Ubuntu是什么?".也可以参考我们的全球网站developer.ubuntu.com/snappy来了解更多信息.
作者:UbuntuTouch 发表于2016/1/12 14:09:45 原文链接
阅读:440 评论:0 查看评论

Read more
UbuntuTouch

在先前的教程"在Ubuntu OS上创建一个dianping Scope (Qt JSON)",我们知道如何使用C++来在Ubuntu平台上开发一个Scope;我们也在文章"使用golang来设计我们的Ubuntu Scope"里展示了如何使用go语言来在Ubuntu上开发一个Scope.在今天的文章中,我们来展示如何利用Javascript语言来开发一个Scope.这对于一些网页开发的开发者来说,无疑是一个天大的好消息,因为你们不需要学习另外一种语言就可以轻松地开发一个属于你们自己的Scope.更多关于Scope开发的知识可以在网址https://developer.ubuntu.com/en/scopes/


1)安装

首先我们必须强调的是Javascrip支持Scope的开发始于Ubuntu 15.04(vivid)系统及以后的版本.在开发之前,开发者必须按照文章"Ubuntu SDK 安装"安装好自己的SDK.同时,必须做如下的JS Scope开发工具的安装:

$ sudo apt install unity-js-scopes-dev
$ unity-js-scopes-tool setup

在这里必须注意的是,我们必须在安装完我们的Ubuntu SDK后才可以执行上面的安装,并在SDK的安装中chroots必须安装完整.经过上面的安装,我们基本上已经完成了我们所有的工具的安装.


2)JS Scope开发文档



所有的开发离不开我们所需要的技术文档.JS Scope的开发文档的地址可以在early build找到.当然你们也可以通过安装unity-js-scopes-doc包来得到帮助.


3)创建一个我们的Scope


Webservice API


我们还是使用我们先前使用百度天气API为例.该API的连接为:


点击上面的连接后,我们可以得到JSON格式的输出:

{"error":0,"status":"success","date":"2016-01-18","results":[{"currentCity":"北京","pm25":"13","index":[{"title":"穿衣","zs":"寒冷","tipt":"穿衣指数","des":"天气寒冷,建议着厚羽绒服、毛皮大衣加厚毛衣等隆冬服装。年老体弱者尤其要注意保暖防冻。"},{"title":"洗车","zs":"较适宜","tipt":"洗车指数","des":"较适宜洗车,未来一天无雨,风力较小,擦洗一新的汽车至少能保持一天。"},{"title":"旅游","zs":"一般","tipt":"旅游指数","des":"天气较好,温度稍低,而且风稍大,让您感觉有些冷,会对外出有一定影响,外出注意防风保暖。"},{"title":"感冒","zs":"极易发","tipt":"感冒指数","des":"天气寒冷,昼夜温差极大且空气湿度较大,易发生感冒,请注意适当增减衣服,加强自我防护避免感冒。"},{"title":"运动","zs":"较不宜","tipt":"运动指数","des":"天气较好,但考虑天气寒冷,风力较强,推荐您进行室内运动,若在户外运动请注意保暖并做好准备活动。"},{"title":"紫外线强度","zs":"弱","tipt":"紫外线强度指数","des":"紫外线强度较弱,建议出门前涂擦SPF在12-15之间、PA+的防晒护肤品。"}],"weather_data":[{"date":"周一 01月18日 (实时:-8℃)","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/qing.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/qing.png","weather":"晴","wind":"北风3-4级","temperature":"-4 ~ -11℃"},{"date":"周二","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/qing.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/duoyun.png","weather":"晴转多云","wind":"微风","temperature":"-1 ~ -8℃"},{"date":"周三","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/duoyun.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/yin.png","weather":"多云转阴","wind":"微风","temperature":"0 ~ -7℃"},{"date":"周四","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/yin.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/duoyun.png","weather":"阴转多云","wind":"微风","temperature":"-3 ~ -6℃"}]}]}

我们的Scope需要来解析上面的JSON格式的输出,并在我们的Scope中呈现.


创建一个最基本的scope


在这一节中,我们来创建一个JS Scope.我们可以利用在Ubuntu SDK中所提供的template来轻松地创建一个Scope.首先,我们打开我们的SDK,并且选择"New File or Project":

     


  


  

  

在最后的几步中,我们必须为每个我们所选择的Kit都要做同样的步骤以完成整个项目的生成.我们这时可以运行(点击SDK左下角的绿色按钮)我们的Scope:



显示如下.基本上没有什么特别的东西.它在默认的情况下显示的是一个天气的Scope,我们可以在它里面输入一些我们所感兴趣的城市的名称来得到当前城市的天气情况.我们可以选择SDK屏幕做下角的Desktop或Ubuntu Desktop SDK kit来在Desktop的环境下运行.当我们需要在手机上运行时,我们必须选择Ubuntu SDK for armhf来运行:


  


项目总览及npm集成:


我们在上面已经生产了我们的项目,我们首先来查看一下我们的项目的结构:

liuxg@liuxg:~/release/chinaweatherjs$ tree
.
├── chinaweatherjs.apparmor
├── CMakeLists.txt
├── CMakeLists.txt.user
├── manifest.json.in
├── po
│   ├── chinaweatherjs.pot
│   ├── CMakeLists.txt
│   ├── Makefile.in.in
│   ├── POTFILES.in
│   └── POTFILES.in.in
└── src
    ├── chinaweatherjs.js
    ├── CMakeLists.txt
    ├── data
    │   ├── chinaweatherjs.ini.in
    │   ├── chinaweatherjs-settings.ini.in
    │   ├── icon.png
    │   └── logo.png
    ├── etc
    └── node_modules
        ├── last-build-arch.txt
        └── unity-js-scopes
            ├── bin
            │   └── unity-js-scopes-launcher
            ├── index.js
            ├── lib
            │   └── scope-core.js
            └── unity_js_scopes_bindings.node

8 directories, 20 files

从上面的结构中,我们可以看出来我们的核心的文件将是src/chinaweatherjs.js文件.在node_modules中含有我们所需要的库.如果你先前已经做过一些Scope开发,那么重新利用该文件来构造自己的Scope将是非常简单的.如果你还没有开发过任何其它的Scope的话,那么,请继续阅读我们下面的介绍.

npm集成

细心的开发者可能已经注意到一个叫做node_modules的目录.JS Scope使用的框架就是npm + Scope.我们可以很方便地使用unity-js-scopes-tool来加入我们所需要的npm包到我们的Scope项目中去.运行的命令如下:

$ unity-js-scopes-tool install <path/to/project/src/node_modules> <npm package> 

上述命令将安装任何一个我们所需要的npm包到我们的项目中去.如果你对npm还不是很熟话,请参阅连接https://www.npmjs.com/


API总览

在这一节中,我们将介绍一下我们所使用的API及如何实现我们所需要的Scope.

Javascript Scope的基本架构

为了能够连接到Scope的runtime,你的Scope只需要遵守几个简单的准则:
  • 导入 Javascript Scope模块到你的代码中
  • 设置你的Scope的runtime上下文
这些步骤简单地说就是如下的代码:

var scopes = require('unity-js-scopes')
scopes.self.initialize({}, {});

一旦被导入,unity-js-scopes核心模块即是和Scope runtime交互的入口点.runtime将帮我们设置好我们的Scope,和我们的Dash进行交互及显示用户在Scope交互所生产的结果等.

在上面的初始化代码中,"self"属性是用来帮助我们实现我们的交互.它引用当前正在运行的Scope的上下文.我们可以在上面显示的index.js文件中看到如下的代码:

Object.defineProperty(
    module.exports,
    "self",
    {
        get: function() {
            if (! self) {
                self = new Scope();
            }
            return self;
        },
    });

除了定义一些你的Scope在运行时的一下runtime元素以外,你的runtime上下文还允许你来检查当前Scope的设置及接受scope runtime环境变化时所生产的变化等.


Runtime 元素

现在,我们可以来重新回顾我们的Scope代码并开始定义一些重要的运行时的函数的行为.
一旦我们的Scope和runtime建立起连接并被用户所启动,scope runtime将发送来所有的由用户所产生的动作.最终这些动作将被发送到有我们的Scope在Initialize过程中所定义的API函数中.

这些API函数可以由我们的Scope来有选择地定义.它们将在runtime时反应出那些最重要的被触发的步骤.下面列举那些最重要的runtime回调函数.

  • run: 当一个scope准备运行时,这个回调函数将被调用.
  • start: 当一个scope准备启动时,这个函数将被调用
  • stop: 当一个scope准备停止时,这个函数将被调用
  • search: 当用户请求一个搜索时,这个函数将被调用.runtime将将提供所有的关于搜索所需要的信息给这个函数的调用.开发者的任务就是通过和runtime的交互把所有可能的结果push给runttime.你也可以控制如何显示这些结果
  • preview: 显示一个在上面search中显示结果的preview.runtime将提供关于这个preview所需要的所有的信息
一个简单的模版为:

var scopes = require('unity-js-scopes')
scopes.self.initialize({}, {
    run: function() {
        console.log('Running...');
    },
    start: function(scope_id) {
        console.log('Starting scope id: ' + scope_id + ', ' + scopes.self.scope_config)
    },
    search: function(canned_query, metadata) {
        return null
    },
    preview: function(result, metadata) {
        return null
    },
}});

对于每一个scope runtime的回调函数来说,它相应于一个用户的交互.scope runtime希望你的scope发送回一个描述各个关键交互所需要的对象.
比如,对search回调函数来说,它希望你的scope发送回一个叫做SearchQuery的object.你将使用这个object来定义用户进行搜索时的行为.
SearchQuery object可以定义一个run回调函数.当搜索发生时,该函数将被调用.同时它也可以定义一个cancel的回调函数.当一个搜索被停止时,该函数将被调用.
Scope runtime同时也传入一个叫做SearchReply的object.这个object可以被用来push一些结果到scope runtime.

上面的这种交互模式是贯穿了整个scope及scope rumtime设计的核心交互模式.


推送搜索结果


上面讲到的一个最核心的搜索交互就是我们的scope可以把我们所需要的结果推送到scope runtime.这些结果是通过SearchReply来完成推送的.这个函数希望一个叫做CategorisedResult类型的数据被创建,并被推送到scope runtime.这个result对象将让我们的scope来定义诸如title, icon,uri等信息.

CategorisedResult的一个额外的功能就是在创建它时,可以指定它结果显示的layout.这个layout是由Category及CategoryRender对象共同定义的.下面就是一个我们天气scope中所使用的一个例子.为了能够获取百度天气API的数据,我们必须重新定义tempalate中的变量:

var query_host = "api.map.baidu.com"
var weather_path = "/telematics/v3/weather?output=json&ak=DdzwVcsGMoYpeg5xQlAFrXQt&location="
var URI = "http://www.weather.com.cn/html/weather/101010100.shtml";

initialize中的search方法定义如下:

                search: function(canned_query, metadata) {
                    return new scopes.lib.SearchQuery(
                                canned_query,
                                metadata,
                                // run
                                function(search_reply) {
                                    var qs = canned_query.query_string();
                                    if (!qs) {
                                        qs = "北京"
                                    }

                                    console.log("query string: " + qs);

                                    var weather_cb = function(response) {
                                        var res = '';

                                        // Another chunk of data has been recieved, so append it to res
                                        response.on('data', function(chunk) {
                                            res += chunk;
                                        });

                                        // The whole response has been recieved
                                        response.on('end', function() {
                                            // console.log("res: " + res);

                                            r = JSON.parse(res);

                                            // Let's get the detailed info
                                            var request_date = r.date
                                            console.log("date: " + date);

                                            var city = r.results[0].currentCity;
                                            console.log("city: " + city);

                                            var pm25 = r.results[0].pm25
                                            console.log("pm25: " + pm25)

                                            var category_renderer = new scopes.lib.CategoryRenderer(JSON.stringify(WEATHER_TEMPLATE));
                                            var category = search_reply.register_category("Chineweather", city, "", category_renderer);

                                            try {
                                                r = JSON.parse(res);
                                                var length = r.results[0].weather_data.length
                                                console.log("length: " + length)

                                                for (var i = 0; i < length; i++) {
                                                    var categorised_result = new scopes.lib.CategorisedResult(category);

                                                    var date = r.results[0].weather_data[i].date
                                                    console.log("date: "+  date);

                                                    var dayPictureUrl = r.results[0].weather_data[i].dayPictureUrl;
                                                    console.log("dayPictureUrl: " + dayPictureUrl);

                                                    var nightPictureUrl = r.results[0].weather_data[i].nightPictureUrl;
                                                    console.log("nightPictureUrl: " + nightPictureUrl);

                                                    var weather = r.results[0].weather_data[i].weather;
                                                    console.log("weather: " + weather);

                                                    var wind = r.results[0].weather_data[i].wind;
                                                    console.log("wind: " + wind);

                                                    var temperature = r.results[0].weather_data[i].temperature;
                                                    console.log("temperature: " + temperature);

                                                    categorised_result.set("weather", weather);
                                                    categorised_result.set("wind", wind);
                                                    categorised_result.set("temperature", temperature);

                                                    categorised_result.set_uri(URI);
                                                    categorised_result.set_title("白天: " + date );
                                                    categorised_result.set_art(dayPictureUrl);
                                                    categorised_result.set("subtitle", weather);
                                                    search_reply.push(categorised_result);

                                                    categorised_result.set_title("夜晚: " + date );
                                                    categorised_result.set_art(nightPictureUrl);
                                                    search_reply.push(categorised_result);

                                                }

                                                // We are done, call finished() on our search_reply
//                                              search_reply.finished();
                                            }
                                            catch(e) {
                                                // Forecast not available
                                                console.log("Forecast for '" + qs + "' is unavailable: " + e)
                                            }
                                        });
                                    }

                                    console.log("request string: " + query_host + weather_path + qs);

                                    http.request({host: query_host, path: weather_path + encode_utf8(qs)}, weather_cb).end();
                                },

                                // cancelled
                                function() {
                                });
                },


Preview搜索结果


一旦我们的搜索结果被推送到scope runtime并被显示,用户可以点击显示的结果并请求一个关于该结果的preview.Scope runtime将通过你的scope中所定义的preview回调来显示所需要的结果.

就像我们上面对search所描述的那样,scope runtime希望你的scope返回一个PreViewQuery的对象来作为一个交互的桥梁.这个对象必须指定一个run及一个cancel的函数.这两个函数和我们上面介绍的search中的语义是一样的.这里不再累述.

对Preview来说,有两个最重要的元素:column layout及Preview Widgets.就像它们的名字所描述的那样,column layout元素是用来定义Preview页面中Preview Component的layout的.Preview Widget是用来在Preview页面中组成页面的.

一旦我们明白了上面所讲的,预览插件并且它被绑定的数据之间的关联是通过“ID”来完成。下面是我们的百度天气里的preview的实现:

  preview: function(result, action_metadata) {
                    return new scopes.lib.PreviewQuery(
                                result,
                                action_metadata,
                                // run
                                function(preview_reply) {
                                    var layout1col = new scopes.lib.ColumnLayout(1);
                                    var layout2col = new scopes.lib.ColumnLayout(2);
                                    var layout3col = new scopes.lib.ColumnLayout(3);
                                    layout1col.add_column(["imageId", "headerId", "temperatureId", "windId"]);

                                    layout2col.add_column(["imageId"]);
                                    layout2col.add_column(["headerId", "temperatureId", "windId"]);

                                    layout3col.add_column(["imageId"]);
                                    layout3col.add_column(["headerId", "temperatureId", "windId"]);
                                    layout3col.add_column([]);

                                    preview_reply.register_layout([layout1col, layout2col, layout3col]);

                                    var header = new scopes.lib.PreviewWidget("headerId", "header");
                                    header.add_attribute_mapping("title", "title");
                                    header.add_attribute_mapping("subtitle", "subtitle");

                                    var image = new scopes.lib.PreviewWidget("imageId", "image");
                                    image.add_attribute_mapping("source", "art");

                                    var temperature = new scopes.lib.PreviewWidget("temperatureId", "text");
                                    temperature.add_attribute_mapping("text", "temperature");

                                    var wind = new scopes.lib.PreviewWidget("windId", "text");
                                    wind.add_attribute_mapping("text", "wind");

                                    preview_reply.push([image, header, temperature, wind ]);
                                    preview_reply.finished();
                                },
                                // cancelled
                                function() {
                                });
                }

我们可以运行我们的Scope:

 



我们可以通过如下的方式来把我们的Scope部署到我们的手机上:




    


对于想学习department的Scope的开发者来说,可以观看我们的培训视频地址.视频中的项目源码.Youtube视频链接
为了能够编译好连接中的例程,我们必须进入到我们的项目中,并安装如下的方法安装github:

$ bzr branch lp:~davidc3/+junk/github-js-scope
$ liuxg@liuxg:~/scope/github-js-scope/src$ unity-js-scopes-tool install ./node_modules github 




   

作者:UbuntuTouch 发表于2016/1/18 15:25:35 原文链接
阅读:391 评论:0 查看评论

Read more
UbuntuTouch

[原]浅析QML语言中的Qt.resolvedUrl

我看过很多的Qt代码,里面有时会用到Qt.resolvedUrl.有时候不仔细想,还真是一知半解.看了Qt的文档

url resolvedUrl(url url)

Returns url resolved relative to the URL of the caller.

你可能还会是一头雾水.这样吧,我们还是利用一个简单的例程来说们问题:


        Image {
            anchors.fill: parent
            source: "images/girl.jpg"

            Component.onCompleted: {
                // This prints 'false'. Although "pics/logo.png" was the input string,
                // it's been converted from a string to a URL, so these two are not the same.
                console.log(source == "images/girl.jpg")

                // This prints 'true' as Qt.resovledUrl() converts the string into a
                // URL with the correctly resolved path
                console.log("resolvedurl: " + Qt.resolvedUrl("images/girl.jpg"))
                console.log(source == Qt.resolvedUrl("images/girl.jpg"))

                // This prints the absolute path, e.g. "file:///path/to/pics/logo.png"
                console.log(source.toString())
            }
        }

上面的例程中,我们在Image中显示另一个女孩的照片.从代码中可以看出来,它明显使用的是相对路径.虽然我们输入的是相对路径,当它被输入到一个url的类型的属性时(比如Image中的source),它将被转换为一个QUrl的对象.如果我们直接把url中的内容和我们输入的内容相比较的话,就会失败.比如上面的代码的输出结果是:




Starting /usr/ubuntu-sdk-dev/bin/qmlscene...
qml: false
qml: resolvedurl: file:///home/liuxg/qml/resolveurl/images/girl.jpg
qml: true
qml: file:///home/liuxg/qml/resolveurl/images/girl.jpg

从上面的代码中可以看出来,使用Qt.resolvedUrl可以把我们的相对路径的url转换为觉得路径的path.

整个项目的源码在:https://github.com/liu-xiao-guo/resolveurl





            
作者:UbuntuTouch 发表于2016/1/25 16:24:22 原文链接
阅读:288 评论:0 查看评论

Read more
UbuntuTouch

在我们写QML应用时,我们如何来优化我们的应用呢?在Ubuntu平台的API中,有一个API叫做"PerformanceOverlay".就像它的名字所说的,它是一个overlay.它可以用来显示一个应用启动或运行时所需要的时间及CPU的使用情况.我们下面来用一个小的应用来测试一下:


import QtQuick 2.0
import Ubuntu.Components 1.1
import Ubuntu.PerformanceMetrics 0.1

/*!
    \brief MainView with a Label and Button elements.
*/

MainView {
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "performanceoverlay.liu-xiao-guo"

    /*
     This property enables the application to change orientation
     when the device is rotated. The default is false.
    */
    //automaticOrientation: true

    // Removes the old toolbar and enables new features of the new header.
    useDeprecatedToolbar: false

    width: units.gu(60)
    height: units.gu(85)

    PerformanceOverlay {
        active: true
    }

    Page {
        title: i18n.tr("PerformanceOverlay")

        Column {
            spacing: units.gu(1)
            anchors {
                margins: units.gu(2)
                fill: parent
            }

            Label {
                id: label
                objectName: "label"

                text: i18n.tr("Hello..")
            }

            Button {
                objectName: "button"
                width: parent.width

                text: i18n.tr("Tap me!")

                onClicked: {
                    label.text = i18n.tr("..world!")
                }
            }
        }
    }
}


当我们运行我们的应用时,在我们的应用的上面显示一个overlay.它是一个漂浮的窗口,我们可以用我们的手指来拖动它,以显示更好的效果.



在上面的图中,我们可以看出CPU的使用率,同时,当我们点击"Tap me"按钮时所显示需要的时间.如果显示为红色,它意味着我们需要做以下事情来优化我们的应用.

所有的源码在https://github.com/liu-xiao-guo/performanceoverlay




作者:UbuntuTouch 发表于2016/1/26 12:07:05 原文链接
阅读:270 评论:0 查看评论

Read more