Canonical Voices

Colin Ian King

Recently I was playing around with CPU loading and was trying to estimate the number of compute operations being executed on my machine.  In particular, I was interested to see how many instructions per cycle and stall cycles I was hitting on the more demanding instructions.   Fortunately, perf stat allows one to get detailed processor statistics to measure this.

In my first test, I wanted to see how the Intel rdrand instruction performed with 2 CPUs loaded (each with a hyper-thread):

$ perf stat stress-ng --rdrand 4 -t 60 --times
stress-ng: info: [7762] dispatching hogs: 4 rdrand
stress-ng: info: [7762] successful run completed in 60.00s
stress-ng: info: [7762] for a 60.00s run time:
stress-ng: info: [7762] 240.01s available CPU time
stress-ng: info: [7762] 231.05s user time ( 96.27%)
stress-ng: info: [7762] 0.11s system time ( 0.05%)
stress-ng: info: [7762] 231.16s total time ( 96.31%)

Performance counter stats for 'stress-ng --rdrand 4 -t 60 --times':

231161.945062 task-clock (msec) # 3.852 CPUs utilized
18,450 context-switches # 0.080 K/sec
92 cpu-migrations # 0.000 K/sec
821 page-faults # 0.004 K/sec
667,745,260,420 cycles # 2.889 GHz
646,960,295,083 stalled-cycles-frontend # 96.89% frontend cycles idle
13,702,533,103 instructions # 0.02 insns per cycle
# 47.21 stalled cycles per insn
6,549,840,185 branches # 28.334 M/sec
2,352,175 branch-misses # 0.04% of all branches

60.006455711 seconds time elapsed

stress-ng's rdrand test just performs a 64 bit rdrand read and loops on this until the data is ready, and performs this 32 times in an unrolled loop.  Perf stat shows that each rdrand + loop sequence on average consumes about 47 stall cycles showing that rdrand is probably just waiting for the PRNG block to produce random data.

My next experiment was to run the stress-ng ackermann stressor; this performs a lot of recursion, hence one should see a predominantly large amount of branching.

$ perf stat stress-ng --cpu 4 --cpu-method ackermann -t 60 --times
stress-ng: info: [7796] dispatching hogs: 4 cpu
stress-ng: info: [7796] successful run completed in 60.03s
stress-ng: info: [7796] for a 60.03s run time:
stress-ng: info: [7796] 240.12s available CPU time
stress-ng: info: [7796] 226.69s user time ( 94.41%)
stress-ng: info: [7796] 0.26s system time ( 0.11%)
stress-ng: info: [7796] 226.95s total time ( 94.52%)

Performance counter stats for 'stress-ng --cpu 4 --cpu-method ackermann -t 60 --times':

226928.278602 task-clock (msec) # 3.780 CPUs utilized
21,752 context-switches # 0.096 K/sec
127 cpu-migrations # 0.001 K/sec
927 page-faults # 0.004 K/sec
594,117,596,619 cycles # 2.618 GHz
298,809,437,018 stalled-cycles-frontend # 50.29% frontend cycles idle
845,746,011,976 instructions # 1.42 insns per cycle
# 0.35 stalled cycles per insn
298,414,546,095 branches # 1315.017 M/sec
95,739,331 branch-misses # 0.03% of all branches

60.032115099 seconds time elapsed about 35% of the time is used in branching and we're getting  about 1.42 instructions per cycle and no many stall cycles, so the code is most probably executing inside the instruction cache, which isn't surprising because the test is rather small.

My final experiment was to measure the stall cycles when performing complex long double floating point math operations, again with stress-ng.

$ perf stat stress-ng --cpu 4 --cpu-method clongdouble -t 60 --times
stress-ng: info: [7854] dispatching hogs: 4 cpu
stress-ng: info: [7854] successful run completed in 60.00s
stress-ng: info: [7854] for a 60.00s run time:
stress-ng: info: [7854] 240.00s available CPU time
stress-ng: info: [7854] 225.15s user time ( 93.81%)
stress-ng: info: [7854] 0.44s system time ( 0.18%)
stress-ng: info: [7854] 225.59s total time ( 93.99%)

Performance counter stats for 'stress-ng --cpu 4 --cpu-method clongdouble -t 60 --times':

225578.329426 task-clock (msec) # 3.757 CPUs utilized
38,443 context-switches # 0.170 K/sec
96 cpu-migrations # 0.000 K/sec
845 page-faults # 0.004 K/sec
651,620,307,394 cycles # 2.889 GHz
521,346,311,902 stalled-cycles-frontend # 80.01% frontend cycles idle
17,079,721,567 instructions # 0.03 insns per cycle
# 30.52 stalled cycles per insn
2,903,757,437 branches # 12.873 M/sec
52,844,177 branch-misses # 1.82% of all branches

60.048819970 seconds time elapsed

The complex math operations take some time to complete, stalling on average over 35 cycles per op.  Instead of using 4 concurrent processes, I re-ran this using just the two CPUs and eliminating 2 of the hyperthreads.  This resulted in 25.4 stall cycles per instruction showing that hyperthreaded processes are stalling because of contention on the floating point units.

Perf stat is an incredibly useful tool for examining performance issues at a very low level.   It is simple to use and yet provides excellent stats to allow one to identify issues and fine tune performance critical code.  Well worth using.

Read more
Daniel Holbach

I Am Who I Am Because Of Who We All Are

I read the “We Are Not Loco” post  a few days ago. I could understand that Randall wanted to further liberate his team in terms of creativity and everything else, but to me it looks feels the wrong approach.

The post makes a simple promise: do away with bureaucracy, rename the team to use a less ambiguous name, JFDI! and things are going to be a lot better. This sounds compelling. We all like simplicity; in a faster and more complicated world we all would like things to be simpler again.

What I can also agree with is the general sense of empowerment. If you’re member of a team somewhere or want to become part of one: go ahead and do awesome things – your team will appreciate your hard work and your ideas.

So what was it in the post that made me sad? It took me a while to find out what specifically it was. The feeling set in when I realised somebody turned their back on a world-wide community and said “all right, we’re doing our own thing – what we used to do together to us is just old baggage”.

Sure, it’s always easier not having to discuss things in a big team. Especially if you want to agree on something like a name or any other small detail this might take ages. On the other hand: the world-wide LoCo community has achieved a lot of fantastic things together: there are lots of coordinated events around the world, there’s the LoCo team portal, and most importantly, there’s a common understanding of what teams can do and we all draw inspiration from each other’s teams. By making this a global initiative we created numerous avenues where new contributors find like-minded individuals (who all live in different places on the globe, but share the same love for Ubuntu and organising local events and activities). Here we can learn from each other, experiment and find out together what the best practices for local community awesomeness are.

Going away and equating the global LoCo community with bureaucracy to me is desolidarisation – it’s quite the opposite of “I Am Who I Am Because Of Who We All Are”.

Personally I would have preferred a set of targeted discussions which try to fix processes, improve communication channels and inspire a new round leaders of Ubuntu LoCo teams. Not everything you do in a LoCo team has to be approved by the entire set of other teams, actual reality in the LoCo world is quite different from that.

If you have ideas to discuss or suggestions, feel free to join our loco-contacts mailing list and bring it up there! It’s your chance to hang out with a lot of fun people from around the globe. :-)

Read more
Dustin Kirkland

I had the great pleasure to deliver a 90 minute talk at the USENIX LISA14 conference, in Seattle, Washington.

During the course of the talk, we managed to:

  • Deploy OpenStack Juno across 6 physical nodes, on an Orange Box on stage
  • Explain all of the major components of OpenStack (Nova, Neutron, Swift, Cinder, Horizon, Keystone, Glance, Ceilometer, Heat, Trove, Sahara)
  • Explore the deployed OpenStack cloud's Horizon interface in depth
  • Configured Neutron networking with internal and external networks, as well as a gateway and a router
  • Setup our security groups to open ICMP and SSH ports
  • Upload an SSH keypair
  • Modify the flavor parameters
  • Update a bunch of quotas
  • Add multiple images to Glance
  • Launch some instances until we max out our hypervisor limits
  • Scale up the Nova Compute nodes from 3 units to 6 units
  • Deploy a real workload (Hadoop + Hive + Kibana + Elastic Search)
  • Then, we deleted the entire environment, and ran it all over again from scratch, non-stop
Slides and a full video are below.  Enjoy!


Read more

My setup:

Laptop with Ubuntu 14.04LTS  and Nexus 4.

I also assume you are comfortable with the command prompt. You need to run some commands from terminal.

If you are already running Android 5.0, you can skip Step 1 and go directly to Step 2 to root your device. In my case, I didn’t wait for the OTA update, but if you prefer to play it safe, get the Android 5.0 update and then start.


Make sure your laptop is charged or plugged into the power and your phone is charged too.

Take a full system backup, because these steps wipes the Android clean.

And ensure your laptop doesn’t go into suspend mode while you do this.

Now install a few packages:

sudo apt-get install android-tools-adb

sudo apt-get install fastboot

sudo add-apt-repository ppa:phablet-team/tools
sudo apt-get update
sudo apt-get install phablet-tools

Step 1: Installing Android 5.0

NOTE: This will wipe the system clean so if you haven’t backed up, go and back up first.

Download the correct file from the Google site:

For Nexus 4 it is called.


Now extract the files, it will show you a directory


Change to this directory occam-lrx21t

Now boot into the bootloader, remember this step as you will need to do it a few times.

adb reboot bootloader

If it’s not able to find the device,  you can boot manually.

For nexus 4, while holding the volume button down, press the power button.

Now unlock the device with the following command.

fastboot oem unlock

Now flash the Android 5.0 Image by running this command from the directory above.


After a few minutes, On the phone you will see a new boot up logo for Android 5.0. this will take a few minutes to complete. Grab a coffee or your favourite beverage.

Once this is complete, wait you still have to root the device.

wait, don’t do much on your android, as the unlock process may wipe your data!

Step 2: Rooting Android.

Download the rooting script from chainfire:

When you scroll down, you see the actual link to download.

This will download this file:

Extract this directory, change to this directory.

Now boot into the boot loader with your preferred method. I used volume down button + power on.

Now type these commands.

chmod +x 

Step 3: Installing MultROM manager

Find the app in Google Play and Install.

Start the app. This will ask you to install 3 things, go ahead and install.


It will also boot into recovery mode.

Once its done, it will reboot and start android.

Now start the MultROM manager again.

You should see the option to install Ubuntu Touch.


Step 4: Installing Ubuntu Touch

If you want demo files, select the -demo

you can choose stable OR development version, you can also install both one by one.

This step took the longest amount of time for me. Go get a nap!

It will ask you to reboot once.


Once this is done, there is no intimation that it is completed.

When you reboot, it will give you an option to boot internal (Android) or Ubuntu Touch. Here you can select Ubuntu Touch and boot into it and setup.

Final Housekeeping. Boot into bootloader and lock the boot loader again:

fastboot oem lock

click on start to reboot your system.

Note: If you upgrade your Android, you will lose the dual-boot and have to start again from step 2 which may differ with your android version.

Read more
Nicholas Skaggs

Virtual Hugs of appreciation!

Because I was asleep at the wheel (err, keyboard) yesterday I failed to express my appreciation for some folks. It's a day for hugging! And I missed it!

I gave everyone a shoutout on social media, but since planet looks best overrun with thank you posts, I shall blog it as well!

Thank you to:

David Planella for being the rock that has anchored the team.
Leo Arias for being super awesome and making testing what it is today on all the core apps.
Carla Sella for working tirelessly on many many different things in the years I've known her. She never gives up (even when I've tried too!), and has many successes to her name for that reason.
Nekhelesh Ramananthan for always being willing to let clock app be the guinea pig
Elfy, for rocking the manual tests project. Seriously awesome work. Everytime you use the tracker, just know elfy has been a part of making that testcase happen.
Jean-Baptiste Lallement and Martin Pitt for making some of my many wishes come true over the years with quality community efforts. Autopkgtest is but one of these.

And many more. Plus some I've forgotten. I can't give hugs to everyone, but I'm willing to try!

To everyone in the ubuntu community, thanks for making ubuntu the wonderful community it is!

Read more
David Henningsson

PulseAudio buffers and protocol

This is a technical post about PulseAudio internals and the upcoming protocol improvements in the upcoming PulseAudio 6.0 release.

PulseAudio memory copies and buffering

PulseAudio is said to have a “zero-copy” architecture. So let’s look at what copies and buffers are involved in a typical playback scenario.

Client side

When PulseAudio server and client runs as the same user, PulseAudio enables shared memory (SHM) for audio data. (In other cases, SHM is disabled for security reasons.) Applications can use pa_stream_begin_write to get a pointer directly into the SHM buffer. When using pa_stream_write or through the ALSA plugin, there will be one memory copy into the SHM.

Server resampling and remapping

On the server side, the server might need to convert the stream into a format that fits the hardware (and potential other streams that might be running simultaneously). This step is skipped if deemed unnecessary.

First, the samples are converted to either signed 16 bit or float 32 bit (mainly depending on resampler requirements).
In case resampling is necessary, we make use of external resampler libraries for this, the default being speex.
Second, if remapping is necessary, e g if the input is mono and the output is stereo, that is performed as well. Finally, the samples are converted to a format that the hardware supports.

So, in worst case, there might be up to four different buffers involved here (first: after converting to “work format”, second: after resampling, third: after remapping, fourth: after converting to hardware supported format), and in best case, this step is entirely skipped.

Mixing and hardware output

PulseAudio’s built in mixer multiplies each channel of each stream with a volume factor and writes the result to the hardware. In case the hardware supports mmap (memory mapping), we write the mix result directly into the DMA buffers.


The best we can do is one copy in total, from the SHM buffer directly into the DMA hardware buffer. I hope this clears up any confusion about what PulseAudio’s advertised “zero copy” capabilities means in practice.

However, memory copies is not the only thing you want to avoid to get good performance, which brings us to the next point:

Protocol improvements in 6.0

PulseAudio does pretty well CPU wise for high latency loads (e g music playback), but a bit worse for low latency loads (e g VOIP, gaming). Or to put it another way, PulseAudio has a low per sample cost, but there is still some optimisation that can be done per packet.

For every playback packet, there are three messages sent: from server to client saying “I need more data”, from client to server saying “here’s some data, I put it in SHM, at this address”, and then a third from server to client saying “thanks, I have no more use for this SHM data, please reclaim the memory”. The third message is not sent until the audio has actually been played back.
For every message, it means syscalls to write, read, and poll a unix socket. This overhead turned out to be significant enough to try to improve.

So instead of putting just the audio data into SHM, as of 6.0 we also put the messages into two SHM ringbuffers, one in each direction. For signalling we use eventfds. (There is also an optimisation layer on top of the eventfd that tries to avoid writing to the eventfd in case no one is currently waiting.) This is not so much for saving memory copies but to save syscalls.

From my own unscientific benchmarks (i e, running “top”), this saves us ~10% – 25% of CPU power in low latency use cases, half of that being on the client side.

Read more
Michael Hall

When things are moving fast and there’s still a lot of work to do, it’s sometimes easy to forget to stop and take the time to say “thank you” to the people that are helping you and the rest of the community. So every November 20th we in Ubuntu have a Community Appreciation Day, to remind us all of the importance of those two little words. We should of course all be saying it every day, but having a reminder like this helps when things get busy.

Like so many who have already posted their appreciation have said, it would be impossible for me to thank everybody I want to thank. Even if I spent all day on this post, I wouldn’t be able to mention even half of them.  So instead I’m going to highlight two people specifically.

First I want to thank Scarlett Clark from the Kubuntu community. In the lead up to this last Ubuntu Online Summit we didn’t have enough track leads on the Users track, which is one that I really wanted to see more active this time around. The track leads from the previous UOS couldn’t do it because of personal or work schedules, and as time was getting scarce I was really in a bind to find someone. I put out a general call for help in one of the Kubuntu IRC channels, and Scarlett was quick to volunteer. I really appreciated her enthusiasm then, and even more the work that she put in as a first-time track lead to help make the Users track a success. So thank you Scarlett.

Next, I really really want to say thank you to Svetlana Belkin, who seems to be contributing in almost every part of Ubuntu these days (including ones I barely know about, like Ubuntu Scientists). She was also a repeat track lead last UOS for the Community track, and has been contributing a lot of great feedback and ideas on ways to make our amazing community even better. Most importantly, in my opinion, is that she’s trying to re-start the Ubuntu Leadership team, which I think is needed now more than ever, and which I really want to become more active in once I get through with some deadline-bound work. I would encourage anybody else who is a leader in the community, or who wants to be one, to join her in that. And thank you, Svetlana, for everything that you do.

It is both a joy and a privilege to be able to work with people like Scarlett and Svetlana, and everybody else in the Ubuntu community. Today more than ever I am reminded about how lucky I am to be a part of it.

Read more
Daniel Holbach

Appreciation for Michael Hall

Today marks another Ubuntu Community Appreciation Day, one of Ubuntu’s beautiful traditions, where you publicly thank people for their work. It’s always hard to pick just one person or a group of people, but you know what – better appreciate somebody’s work than nobody’s work at all.

One person I’d like to thanks for their work is Michael Hall. He is always around, always working on a number of projects, always involved in discussions on social media and never shy to add yet another work item to his TODO list. Even with big projects on his plate, he is still writing apps, blog entries, charms and hacks on a number of websites and is still on top of things like mailing list discussions.

I don’t know how he does it, but I’m astounded how he gets things done and still stays friendly. I’m glad he’s part of our team and tirelessly working on making Ubuntu a better place.

I also like this picture of him.


Mike: keep up the good work! :-)

Read more

[原]Ubuntu OS应用Runtime Enviroment

在这篇文章中,我们将介绍Ubuntu OS的Runtime Environment。在文章“App confinement: Security policy for click packages”中,我们看见它里面有介绍一个应用的runtime环境。这里,我们通过一个例子来显示一个应用的runtime环境到底是怎样的。


bzr branch lp:~liu-xiao-guo/debiantrial/readenv


import QtQuick 2.0
import Ubuntu.Components 0.1
import Ubuntu.Components.ListItems 0.1 as ListItem
import ReadEnv 1.0
import "ui"

    \brief MainView with Tabs element.
           First Tab has a single Label and
           second Tab has a single ToolbarAction.

MainView {
    id: root

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

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

    anchorToKeyboard: true

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


    property string app_pkgname

    ReadEnv {
        id: readEnv

    Flickable {
        id: scrollWidget
        anchors.fill: parent
        contentHeight: contentItem.childrenRect.height
        boundsBehavior: (contentHeight > root.height) ? Flickable.DragAndOvershootBounds : Flickable.StopAtBounds
        /* Set the direction to workaround
           otherwise the UI might end up in a situation where scrolling doesn't work */
        flickableDirection: Flickable.VerticalFlick

        Column {
            anchors.left: parent.left
            anchors.right: parent.right

            ListItem.Base {
                height: ubuntuLabel.height + runtime.height +

                Column {
                    anchors.left: parent.left
                    anchors.right: parent.right
                    anchors.centerIn: parent
                    Label {
                        id: ubuntuLabel
                        anchors.horizontalCenter: parent.horizontalCenter
                        text: ""
                        fontSize: "x-large"
                    Label {
                        id: runtime
                        anchors.horizontalCenter: parent.horizontalCenter
                        text: "Runtime Environment"

            ListItem.Subtitled {
                subText: readEnv.getenv("UBUNTU_APPLICATION_ISOLATION")

            ListItem.Subtitled {
                subText: readEnv.getenv("APP_ID")

            ListItem.Subtitled {
                subText: readEnv.getenv("XDG_CACHE_HOME")

            ListItem.Subtitled {
                subText: readEnv.getenv("XDG_CONFIG_HOME")

            ListItem.Subtitled {
                subText: readEnv.getenv("XDG_DATA_HOME")

            ListItem.Subtitled {
                subText: readEnv.getenv("XDG_RUNTIME_DIR")

            ListItem.Subtitled {
                subText: readEnv.getenv("TMPDIR")

            ListItem.Subtitled {
                subText: readEnv.getenv("PWD")

            ListItem.Subtitled {
                subText: app_pkgname

            ListItem.Subtitled {
                subText: readEnv.getenv("PATH")

            ListItem.Subtitled {
                subText: readEnv.getenv("LD_LIBRARY_PATH")

            ListItem.Subtitled {
                subText: readEnv.getenv("QML2_IMPORT_PATH")

    Component.onCompleted: {
        var APP_ID = readEnv.getenv("APP_ID");

        console.log("APP_ID: " + APP_ID );
        app_pkgname = APP_ID.split('_')[0]
        console.log("APP_PKGNAME: " + app_pkgname);





  • /home/phablet/.cache/com.ubuntu.developer.liu-xiao-guo.readenv
  • /home/phablet/.config/com.ubuntu.developer.liu-xiao-guo.readenv
  • /home/phablet/.local/share/com.ubuntu.developer.liu-xiao-guo.readenv
  • /run/user/32011/confined/com.ubuntu.developer.liu-xiao-guo.readenv


bzr branch lp:~liu-xiao-guo/debiantrial/runtimeevn

作者:UbuntuTouch 发表于2014-11-19 14:52:03 原文链接
阅读:312 评论:0 查看评论

Read more
Joseph Salisbury

Meeting Minutes

IRC Log of the meeting.

Meeting minutes.


20141118 Meeting Agenda

Release Metrics and Incoming Bugs

Release metrics and incoming bug data can be reviewed at the following link:


Status: Vivid Development Kernel

The master-next branch of our Vivid kernel has been rebased to the
v3.18-rc5 upstream kernel. We have still withheld uploading to
the archive until we’ve progressed to a later -rc candidate.
Important upcoming dates:
Thurs Dec 18 – Vivid Alpha 1 (~4 weeks away)
Thurs Jan 22 – Vivid Alpha 2 (~9 weeks away)

Status: CVE’s

The current CVE status can be reviewed at the following link:

Status: Stable, Security, and Bugfix Kernel Updates – Utopic/Trusty/Precise/Lucid

Status for the main kernels, until today (18-Nov):

  • Lucid – Verification & Testing
  • Precise – Verification & Testing
  • Trusty – Verification & Testing
  • Utopic – Verification & Testing

    Current opened tracking bugs details:


    For SRUs, SRU report is a good source of information:



    cycle: 31-Oct through 22-Nov
    31-Oct Last day for kernel commits for this cycle
    02-Nov – 08-Nov Kernel prep week.
    09-Nov – 15-Nov Bug verification & Regression testing.
    16-Nov – 22-Nov Regression testing & Release to -updates.

Open Discussion or Questions? Raise your hand to be recognized

No open discussions.

Read more

PyCon 2014 en Rafaela

Acaba de pasar la sexta PyCon Argentina. Como dice el título, se hizo en Rafaela, provincia de Santa Fe.

Fuimos con Nico Demarchi en auto, salimos el miércoles a la tarde y llegamos una y monedas de la mañana, volvimos el domingo durante el día, arrancando a media mañana. Creo que es el límite de lo que haría en auto... más distancia ya iría en micro o avión.

Yo tenía que llegar el miércoles a la noche porque el jueves abría el día de talleres con Introducción a Python (modo charla extendida, ya que tenía dos horas). El jueves dí dos charlas más: Cómo debuguear en Python, y Cómo los logs me salvaron el alma.Y para cerrar (justo antes de los sorteos y foto grupal), le conté a la gente un poco cómo íbamos con el proyecto de armar la Asociación Civil de PyAr.

Mis charlas salieron bien, aunque la de debugging no me gustó del todo como la había dado (pero luego recibí buen feedback). Para el taller de Intro a Python usé por primera vez a Pysenteishon, un software muy copado y piola para ir pasando los slides desde el teléfono (¡gracias Emiliano por hacerlo!). Y para las charlas del jueves estuve por primera vez descalzo dando la presentación (era algo que quería probar desde hace rato, y aproveché que el escenario del auditorio tenía piso de madera).

Dando la charla en patas

También fuí a muchas charlas, había muchas cosas copadas para ver, y creo que me salté uno o dos timeslots nada más en toda la conferencia. Las keynotes estuvieron bien, pero no me entusiasmaron particularmente. Y todo lo fue lugar y organización estuvo genial, la verdad que se pasaron. Lo mismo con la gente con la que me (re)encontré: es un placer ser parte de una comunidad así.

Yo llevé la cámara... pero la verdad es que colgué sacando fotos. Pero la grosa de Yami sacó un montón, están todas acá. Y una de las últimas que sacó es justamente la grupal, esta que muestro acá:

Foto grupal

Y como siempre que uno no viaja durmiendo o solo, está el efecto de "PyCon extendida". Es que uno viene charlando de mil cosas, de lo más variado, pero también de proyectos, ideas, etc. Con Nico nos venía rondando en la cabeza una idea para facilitar el uso de dependencias en programas Python, estuvimos charlando con gente en la conferencia, nos dieron feedback, la idea fue mutando... y en el viaje de vuelta se nos terminó de ocurrir algo piola, que no debería ser demasiado loco de implementar; ya les traeré la novedad.

¡Pero no sólo un proyecto me traje! (como si tuviera pocos y/o mucho tiempo libre, ¿no?). Tengo ganas de hacer una "maquinita de timelapse" con una Raspi (una cajita que uno puede colgar en cualquier lado y dejarla ahí algunas horas o un par de días y arme un video de esos donde se ve todo rápido, por ejemplo este). El otro proyecto es armar una valija o caja robusta con todo lo necesario en un PyCamp (router, computadora para caché de repositorios, energía, y varios etcéteras), de manera de tener todo listo y de fácil armado, onda llegás y enchufás. Ya veremos cómo se van desarrollando ambos proyectos...

Read more
Michael Hall

Last week was our second ever Ubuntu Online Summit, and it couldn’t have gone better. Not only was it a great chance for us in Canonical to talk about what we’re working on and get community members involved in the ongoing work, it was also an opportunity for the community to show us what they have been working on and give us an opportunity to get involved with them.

Community Track leads

This was also the second time we’ve recruited track leads from among the community. Traditionally leading a track was a responsibility given to one of the engineering managers within Canonical, and it was up to them to decide what sessions to put on the UDS schedule. We kept the same basic approach when we went to online vUDS. But starting with UOS 14.06, we asked leaders in the community to help us with that, and they’ve done a phenomenal job. This time we had Nekhelesh RamananthanJosé Antonio ReySvetlana BelkinRohan GargElfy, and Scarlett Clark take up that call, and they were instrumental in getting even more of the community involved

Community Session Hosts

uos_creatorsMore than a third of those who created sessions for this UOS were from the community, not Canonical. For comparison, in the last in-person UDS, less than a quarter of session creators were non-Canonical. The shift online has been disruptive, and we’ve tried many variations to try and find what works, but this metric shows that those efforts are starting to pay off. Community involvement, indeed community direction, is higher in these Online Summits than it was in UDS. This is becoming a true community event: community focused, community organized, and community run.

Community Initiatives

The Ubuntu Online Summit wasn’t just about the projects driven by Canonical, such as the Ubuntu desktop and phone, there were many sessions about projects started and driven by members of the community. Last week we were shown the latest development on Ubuntu MATE and KDE Plasma 5 from non-Canonical lead flavors. We saw a whole set of planning sessions for community developed Core Apps and an exciting new Component Store for app developers to share bits of code with each other. For outreach there were sessions for providing localized ISOs for loco teams and expanding the scope of the community-lead Start Ubuntu project. Finally we had someone from the community kick off a serious discussion about getting Ubuntu running on cars. Cars! All of these exciting sessions were thought up by, proposed by, and run by members of the community.

Community Improvements

This was a great Ubuntu Online Summit, and I was certainly happy with the increased level of community involvement in it, but we still have room to make it better. And we are going to make it better with help from the community. We will be sending out a survey to everyone who registered as attending for this UOS to gather feedback and ideas, please take the time to fill it out when you get the link. If you attended but didn’t register there’s still time, go to the link above, log in and save your attendance record. Finally, it’s never too early to start thinking about the next UOS and what sessions you might want to lead for it, so that you’re prepared when those track leads come knocking at your door.

Read more

Last night I did a trivial yet surprisingly satisfying hardware hack, of the kind that can only be accomplished when the brain is in holiday mode. Our son has that very simple airplane toy, which turned out to become one of his most loved ones, enough to have deserved wheel repairs before. He’s also reportedly a fan of all kinds of light-emitting or reflecting objects (including the sun, and specially the moon). So the idea sparkled: how easily can that airplane get a blinking led?

With an attiny85, a CR2032 battery, a LED, and half an hour of soldering work, this was the result:

The code loaded in the chip is small enough to be listed here, and it gets away with blinking without waking up the main CPU clock:

    ; Set inverse OC1B pin as output for the led.
    sbi _SFR_IO_ADDR(DDRB), 3

    ; Enable timer TC1 with PCK/16k prescaling (attiny85 p.89)
    ldi r18, (1<<CS10)|(1<<CS11)|(1<<CS12)|(1<<CS13)
    out _SFR_IO_ADDR(TCCR1), r18

    ; Set OC1B on compare match (250), clear on 0x00 (attiny85 p.86,90)
    ldi r18, (1<<PWM1B) | (1<<COM1B0)
    out _SFR_IO_ADDR(GTCCR), r18
    ldi r18, 250
    out _SFR_IO_ADDR(OCR1B), r18

    ; Set the sleep mode to idle (attiny85 p.39).
    ldi r18, (1<<SE)
    out _SFR_IO_ADDR(MCUCR), r18

    ; Shutdown unnecessary MCU modules (attiny85 p.38)
    ldi r18, (1<<PRTIM0)|(1<<PRUSI)|(1<<PRADC)
    out _SFR_IO_ADDR(PRR), r18

    rjmp .-4

The power consumption in the idle mode plus the blinks should keep the coin battery running for a couple of weeks, at least. A vibration sensor would improve that significantly, by enabling the MCU to go into powerdown mode and be awaken externally, but I don’t have a sensor at hand that is small enough.

This is the assembly, and the final result:

Toy Airplane Hack

He’s enjoying it. :-)

Read more

A couple of people have reached out to me via LinkedIn and reminded me that my three year work anniversary happened last Friday. Three years since I left my job at a local place to go work for the Canonical where I got the chance to be paid to work on open source software and better my Python skills with the team working on Launchpad. My wife wasn’t quite sure. “You’ve only been at your job a year and a half, and your last one was only two years. What makes this different?”

What’s amazing, looking back, is just how *right* the decision turned out to be. I was nervous at the time. I really wasn’t Launchpad’s biggest fan. However, the team I interviewed with held this promise of making me a better developer. They were doing code reviews of every branch that went up to land. They had automated testing, and they firmly believed in unit and functional tests of the code. It was a case of the product didn’t excite me, but the environment, working with smart developers from across the globe, was exactly what I felt like I needed to move forward with my career, my craft.

2013-09-02 18.17.47

I joined my team on Launchpad in a squad of four other developers. It was funny. When I joined I felt so lost. Launchpad is an amazing and huge bit of software, and I knew I was in over my head. I talked with my manager at the time, Deryck, and he told me “Don’t worry, it’ll take you about a year to get really productive working on Launchpad.” A year! Surely you jest, and if you’re not jesting…wtf did I just get myself into?

It was a long road and over time I learned how to take a code review (a really hard skill for many of us), how to do one, and how to talk with other smart and opinionated developers. I learned the value of the daily standup, how to manage work across a kanban board. I learned to really learn from others. Up until this point I’d always been the big fish in a small pond and suddenly I was the minnow hiding in the shallows. Forget books on how to code, just look at the diff in the code review you’re reading right now. Learn!

My boss was right, it was nearly ten months before I really felt like I could be asked to do most things in Launchpad and get them done in an efficient way. Soon our team was moved on from Launchpad to other projects. It was actually pretty great. On the one hand, “Hey! I just got the hang of this thing” but, on the other hand, we were moving on to new things. Development life here has never been one of sitting still. We sit down and work on the Ubuntu cycle of six month plans, and it’s funny because even that is such a long time. Do you really know what you’ll be doing six months from now?


Since that time in Launchpad I’ve gotten work on several different projects and I ended up switching teams to work on the Juju Gui. I didn’t really know a lot about this Juju thing, but the Gui was a fascinating project. It’s a really large scale JavaScript application. This is no “toss some jQuery on a web page” thing here.

I also moved to work under a new manager Gary. As my second manager since starting at Canonical and I was amazed at my luck. Here I’ve had two great mentors that made huge strides in teaching me how to work with other developers, how to do the fun stuff, the mundane, and how to take pride in the accomplishments of the team. I sit down at my computer every day and I’ve got the brain power of amazing people at my disposal over irc, Google Hangouts, email, and more. It’s amazing to think that at these sprints we do, I’m pretty much never the smartest person in the room. However, that’s what’s so great. It’s never boring and when there’s a problem the key is that we put our joint brilliant minds to the problem. In every hard problem we’ve faced I’ve never found that a single person had the one true solution. What we come up with together is always better than what any of us had apart.

When Gary left and there was a void for team lead and it was something I was interested in. I really can’t say enough awesome things about the team of folks I work with. I wanted to keep us all together and I felt like it would be great for us to try to keep things going. It was kind of a “well I’ll just try not to $#@$@# it up” situation. That was more than nine months ago now. Gary and Deryck taught me so much, and I still have to bite my tongue and ask myself “What would Gary do” at times. I’ve kept some things the same, but I’ve also brought my own flavor into the team a bit, at least I like to think so. These days my Github profile doesn’t show me landing a branch a day, but I take great pride in the progress of the team as a whole each and every week.

The team I run now is as awesome a group of people, the best I could hope to work for. I do mean that, I work for my team. It’s never the other way around and that’s one lesson I definitely picked up from my previous leads. The projects we’re working on are exciting and new and are really important to Canonical. I get to sit in and have discussions and planning meetings with Canonical super genius veterans like Kapil, Gustavo, and occasionally Mark Shuttleworth himself.

Looking back I’ve spent the last three years becoming a better developer, getting an on the job training course on leading a team of brilliant people, and crash course on thinking about the project, not just as the bugs or features for the week, but for the project as it needs to exist in three to six months. I’ve spent three years bouncing between “what have I gotten myself into, this is beyond my abilities” to “I’ve got this. You can’t find someone else to do this better”. I always tell people that if you’re not swimming as hard as you can to keep up, find another job. I feel like three years ago I did that and I’ve been swimming ever since.


Three years is a long time in a career these days. It’s been a wild ride and I can’t thank the folks that let me in the door, taught me, and have given me the power to do great things with my work enough. I’ve worked by butt off in Budapest, Copenhagen, Cape Town, Brussels, North Carolina, London, Vegas, and the bay area a few times. Will I be here three years from now? Who knows, but I know I’ve got an awesome team to work with on Monday and we’ll be building an awesome product to keep building. I’m going to really enjoy doing work that’s challenging and fulfilling every step of the way.


Read more

Tracking Usage

One of the long standing goals of Unity has been to provide an application focused presentation of the desktop. Under X11 this proves tricky as anyone can connect into X and doesn't necessarily have to give information on what applications they're associated with. So we wrote BAMF, which does a pretty good job of matching windows to applications, but it could never be perfect because there simply wasn't enough information available. When we started to rethink the world assuming a non-X11 display server we knew there was one thing we really wanted, to never ever have something like BAMF again.

This meant designing, from startup to shutdown, a complete tracking of an application before it started creating windows in the display server. We then were able to use the same mechanisms to create a consistent and secure environment for the applications. This is both good for developers and users as their applications start in a predictable way each and every time it's started. And we also setup the per-application AppArmor confinement that the application lives in.

Enough backstory, what's really important to this blog post is that we also get an event when an application starts and stops which is a reliable event. So I wrote a little tool that takes those events out of the log and presents them as usage data. It is cleverly called:

$ ubuntu-app-usage

And it presents a list of all the applications that you've used on the system along with how long you've used them. How long do you spend messing around on the web? Now you know. You're welcome.

It's not perfect in that it uses all the time that you've used the device, it'd be nice to query the last week or the last year to see that data as well. Perhaps even a percentage of time. I might add those little things in the future, if you're interested you can beat me too it.

Read more

.NET goes open source

With other languages such as Java and Python getting large user base, this is a good move to gain some users.

Microsoft is open sourcing .NET, this should reduce the compatibility challenges with cross-platform applications developed using Microsoft Tools.

Read more




这个Scope的开发视频可以在地址“如何在Ubuntu OS上开发Scope (视频)”找到。


我们打开我们的SDK。我们选择菜单“File”==>“New File or Project”。然后我们选择“Unity Scope”。输入“dianping”作为我们项目的名字。


我们选择适合自己的“Qt Scope using HTTP+JSON API”模版,并输入我们的Scope名字。




我们可以运行一下我们刚创建的Scope。运行的情况如下。如果我们有问题的话,我们可能需要参照文章“Ubuntu SDK 安装Qt SDK安装”。


我们可以使用热键“Ctrl + R”来在Desktop下运行我们的Scope。也可以点击SDK中做下角的绿色的按钮就可以直接运行我们的Scope了。如果在“Application Output”显示的是“scope-unit-tests”,我们必须按照如下的方法进行修改以使得它能够在我们的Desktop下面能够正常运行:



这个模版的Scope是一个完整的Scope,我们可以在“Search Query”中输入“Beijing”看看有什么变化。





在整个template的设计中,Client的作用是用来向Web Service申请数据,并把数据存入内存中以便在Query的类中调用,并用Scope UI toolkit进行展示。最初的设计和我们的是不一样的。为了方便我们的设计,我们把Client类的接口进行修改,如下:

#ifndef API_CLIENT_H_
#define API_CLIENT_H_

#include <api/config.h>

#include <atomic>
#include <deque>
#include <map>
#include <string>
#include <core/net/http/request.h>
#include <core/net/uri.h>

#include <QJsonDocument>

namespace api {

 * Provide a nice way to access the HTTP API.
 * We don't want our scope's code to be mixed together with HTTP and JSON handling.
class Client {

     * Data structure for the quried data
    struct Data {
        std::string name;
        std::string business_url;
        std::string s_photo_url;
        std::string photo_url;
        std::string rating_s_img_url;
        std::string address;
        std::string telephone;

     * A list of weather information
    typedef std::deque<Data> DataList;

    Client(Config::Ptr config);

    virtual ~Client() = default;

     * Get the query data
    virtual DataList getData(const std::string &query);

     * Cancel any pending queries (this method can be called from a different thread)
    virtual void cancel();

    virtual Config::Ptr config();

    void get(QString uri, QJsonDocument &root);

     * Progress callback that allows the query to cancel pending HTTP requests.
    core::net::http::Request::Progress::Next progress_report(
            const core::net::http::Request::Progress& progress);

    QString getQueryString(QString query);
    QString removeTestInfo(QString name);

     * Hang onto the configuration information
    Config::Ptr config_;

     * Thread-safe cancelled flag
    std::atomic<bool> cancelled_;


#endif // API_CLIENT_H_


    virtual DataList getData(const std::string &query);


#include <api/client.h>

#include <core/net/error.h>
#include <core/net/http/client.h>
#include <core/net/http/content_type.h>
#include <core/net/http/response.h>
#include <QVariantMap>

#include <QString>
#include <QCryptographicHash>
#include <QDebug>
#include <QTextCodec>
#include <QUrl>

#include <iostream>

namespace http = core::net::http;
namespace net = core::net;

using namespace api;
using namespace std;
using namespace std;

const QString appkey = "3562917596";
const QString secret = "091bf584e9d24edbbf48971d65307be3";
const QString BASE_URI = "";

Client::Client(Config::Ptr config) :
    config_(config), cancelled_(false) {

void Client::get( QString uri, QJsonDocument &root) {
    // Create a new HTTP client
    auto client = http::make_client();

    // Start building the request configuration
    http::Request::Configuration configuration;

    // Build the URI from its components
    configuration.uri = uri.toStdString();

    // Give out a user agent string
    configuration.header.add("User-Agent", config_->user_agent);

    // Build a HTTP request object from our configuration
    auto request = client->head(configuration);

    try {
        // Synchronously make the HTTP request
        // We bind the cancellable callback to #progress_report
        auto response = request->execute(
                    bind(&Client::progress_report, this, placeholders::_1));

        // Check that we got a sensible HTTP status code
        if (response.status != http::Status::ok) {
            throw domain_error(response.body);
        // Parse the JSON from the response
        root = QJsonDocument::fromJson(response.body.c_str());

        // qDebug() << "response: " << response.body.c_str();
    } catch (net::Error &) {

Client::DataList Client::getData(const string& query) {
    QJsonDocument root;

    QString temp = QString::fromStdString(query);
    QByteArray bytearray = query.c_str();
    QString query_string = QString::fromUtf8(, bytearray.size());

    qDebug() << "query_string: " << query_string;

    QString uri = getQueryString(query_string);
    qDebug() << "uri: "  << uri;
    get(uri, root);

    DataList result;

    QVariantMap variant = root.toVariant().toMap();

    // Iterate through the weather data
    for (const QVariant &i : variant["businesses"].toList()) {
        QVariantMap item = i.toMap();

        QString name = removeTestInfo(item["name"].toString());
        qDebug() << "name: " << name;

        QString business_url = item["business_url"].toString();
        qDebug() << "business_url: " << business_url;

        QString s_photo_url = item["s_photo_url"].toString();
        qDebug() << "s_photo_url: " << s_photo_url;

        QString photo_url = item["photo_url"].toString();
        qDebug() << "photo_url: " << photo_url;

        QString rating_s_img_url = item["rating_s_img_url"].toString();
        qDebug() << "rating_s_img_url: " << rating_s_img_url;

        QString address = item["address"].toString();
        qDebug() << "address: " << address;

        QString telephone = item["telephone"].toString();
        qDebug() << "telephone: " << telephone;

        // Add a result to the weather list
            Data { name.toStdString(), business_url.toStdString(), s_photo_url.toStdString(),
                   photo_url.toStdString(), rating_s_img_url.toStdString(),
                   address.toStdString(), telephone.toStdString() });

    return result;

http::Request::Progress::Next Client::progress_report(
        const http::Request::Progress&) {

    return cancelled_ ?
                http::Request::Progress::Next::abort_operation :

void Client::cancel() {
    cancelled_ = true;

Config::Ptr Client::config() {
    return config_;

QString Client::getQueryString(QString query) {
    QMap<QString, QString> map;

    map["category"] = "美食";
    map["city"] = query;
    map["sort"] = "2";
    map["limit"] = "20";
    map["platform"] = "2";

    QCryptographicHash generator(QCryptographicHash::Sha1);

    QString temp;
    QMapIterator<QString, QString> i(map);
    while (i.hasNext()) {;
        qDebug() << i.key() << ": " << i.value();


    qDebug() << temp;

    qDebug() << "UTF-8: " << temp.toUtf8();

    QString sign = generator.result().toHex().toUpper();

//    qDebug() << sign;

    QString url;


    while (i.hasNext()) {;
        qDebug() << i.key() << ": " << i.value();

    qDebug() << "Final url: " << url;

    QUrl url1(url);

    qDebug() << url1.url();
    QByteArray bytearray = url1.toEncoded();
    QString string = QString::fromUtf8(, bytearray.size());
    qDebug() << "url new: " << string;

    return string;

// The following method is used to remove the
// "这是一条测试商户数据,仅用于测试开发,开发完成后请申请正式数据..." string
const QString TEST_STRING = "(这是一条测试商户数据,仅用于测试开发,开发完成后请申请正式数据...)";
QString Client::removeTestInfo(QString name)
    if ( name.contains(TEST_STRING) ) {
        int index = name.indexOf(TEST_STRING);
        QString newName = name.left(index);
        // qDebug() << "newName: " << newName;
        return newName;
    } else {
        qDebug() << "it does not contain the string";
        return name;

首先,我们可以看一下这里的helper方法“getQueryString”。这个方法的作用是用来得到我们的申请的url的。我们可以参考“点评开发入门示例”是怎么生成申请的url的。“removeTestInfo”是用来删除我们得到的数据的部分内容以更好地呈现。如果我们需要正式的数据,需要和dianping进行注册。我们通过使用能够net-cpp的API来进行HTTP请求,并使用Qt API QJsonDocument来进行JSON的解析。我们可以通过qDebug来打印并输出我们想要看到的内容。


void Query::run(sc::SearchReplyProxy const& reply) {
    try {
        // Start by getting information about the query
        const sc::CannedQuery &query(sc::SearchQueryBase::query());
        QString queryString = QString::fromStdString(query.query_string());

        // Trim the query string of whitespace
        string query_string = alg::trim_copy(query.query_string());

        Client::DataList datalist;
        if (query_string.empty()) {
            queryString = QString("北京");
            datalist = client_.getData("北京");
        } else {
            // otherwise, get the forecast for the search string
            datalist = client_.getData(query_string);
    } catch (domain_error &e) {
        // Handle exceptions being thrown by the client API
        cerr << e.what() << endl;


这样我们的Client类的实现基本已经完成。它可以帮我们向点评的web service发送请求,并得到我们想要的数据。这个数据被放在一个叫做Client::DataList的数组中。我们在下面的章节中具体描述怎么利用这个数据进行显示。通过Client类的实现,我们完成了数据(Client)和展示(Query)之间的分离。这也是一个比较推荐的设计。


bzr branch lp:~liu-xiao-guo/debiantrial/dianpianclient1



  • 这个类定义了“start", "stop"及"run"来运行scope。绝大多数开发者并不需要修改这个类的大部分实现。在我们的例程中,我们将不做任何的修改
  • 它也同时实现了另外的两个方法:search 和 preview。我们一般来说不需要修改这俩个方法的实现。但是他们所调用的函数在具体的文件中必须实现
注意:我们可以通过研究Scope API的头文件来对API有更多的认识。更多的详细描述,开发者可以在查看。



  • 得到由用户输入的查询字符串
  • 向web services发送请求
  • 生成搜索的结果(根据每个不同而不同)
  • 创建搜索结果category(比如不同的layout-- grid/carousel)
  • 根据不同的搜寻结果来绑定不同的category以显示我们所需要的UI
  • 推送不同的category来显示给最终用户


在本例中,我们创建了两个JSON objects. 它们是最原始的字符串,如下所示,它有两个field:template及components。template是用来定义是用什么layout来显示我们所搜索到的结果。这里我们选择的是”grid"和carousel布局。用的是小的card-size。components项可以用来让我们选择预先定义好的field来显示我们所需要的结果。这里我们添加了"title"及“art"。同时我们可以显示一个title及一个图片(art)。更多详细的介绍可以参阅文章“Customization & branding”。

//Create a JSON string to be used tro create a category renderer - uses grid layout
std::string CR_GRID = R"(
        "schema-version" : 1,
        "template" : {
        "category-layout" : "grid",
        "card-size": "small"
        "components" : {
        "title" : "title",
        "art" : {
        "field": "art",
        "aspect-ratio": 1.6,
        "fill-mode": "fit"

//Create a JSON string to be used tro create a category renderer - uses carousel layout
std::string CR_CAROUSEL = R"(
        "schema-version" : 1,
        "template" : {
        "category-layout" : "carousel",
        "card-size": "small",
        "overlay" : true
        "components" : {
        "title" : "title",
        "art" : {
        "field": "art",
        "aspect-ratio": 1.6,
        "fill-mode": "fit"

我们把上述的定义加到我们Query.cpp文件的开始部分。同时我们也可以删除由template已经创建好的“WEATHER_TEMPLATE”及“CITY_TEMPLATE”定义。它们被定义在文件的起始部分。更多关于 CategoryRenderer 类的介绍可以在docs找到。

我们为每个JSON Object创建了一个CategoryRenderer,并同时向reply object注册。我们修改我们的run方法来实现显示:

void Query::run(sc::SearchReplyProxy const& reply) {
    try {
        // Start by getting information about the query
        const sc::CannedQuery &query(sc::SearchQueryBase::query());
        QString queryString = QString::fromStdString(query.query_string());

        // Trim the query string of whitespace
        string query_string = alg::trim_copy(query.query_string());

        Client::DataList datalist;
        if (query_string.empty()) {
            queryString = QString("北京");
            datalist = client_.getData("北京");
        } else {
            // otherwise, get the forecast for the search string
            datalist = client_.getData(query_string);

        CategoryRenderer rdrGrid(CR_GRID);
        CategoryRenderer rdrCarousel(CR_CAROUSEL);

        QString title = queryString + "美味";

        // Register two categories
        auto carousel = reply->register_category("dianpingcarousel", title.toStdString(), "", rdrCarousel);
        auto grid = reply->register_category("dianpinggrid", "", "", rdrGrid);

        bool isgrid = false;

        // For each of the entry in the datalist
        for (const Client::Data &data : datalist) {

            // for each result
            const std::shared_ptr<const Category> * category;

            if ( isgrid ) {
                category = &grid;
                isgrid = false;
            } else {
                isgrid = true;
                category = &carousel;

            //create our categorized result using our pointer, which is either to out
            //grid or our carousel Category
            CategorisedResult catres((*category));

            // We must have a URI

            catres["address"] = Variant(data.address);
            catres["telephone"] = Variant(data.telephone);

            // Push the result
            if (!reply->push(catres)) {
                // If we fail to push, it means the query has been cancelled.
                // So don't continue;
    } catch (domain_error &e) {
        // Handle exceptions being thrown by the client API
        cerr << e.what() << endl;


using namespace unity::scopes;

我们从我们的Client API中的Client::getData来获取我们所需要的web service的数据,把数据填入到相应的CategorisedResult中。重新编译我们的Scope,我们可以看到如下的画面:

我们可以在“Search Query”中输入“上海”,我们可以看到不同的结果。我们也可以尝试点击我们的画面,在另外一个画面中可以看到一个图片。

bzr branch lp:~liu-xiao-guo/debiantrial/dianpianclient2



  • 定义在preview时所需要的widget
  • 让widget和搜索到的数据field一一对应起来
  • 定义不同数量的layout列(由屏幕的尺寸来定)
  • 把不同的widget分配到layout中的不同列中
  • 把reply实例显示到layout的widget中



Preview Widgets

这是一组预先定义好的widgets。每个都有一个类型。更据这个类型我们可以生成它们。你可以在这里找到Preview Widget列表及它们提供的的field类型。


  • header:它有title及subtitle field
  • image:它有source field有来显示从哪里得到这个art
  • text:它有text field
  • action:用来展示一个有"Open"的按钮。当用户点击时,所包含的URI将被打开
    PreviewWidget w_header("headerId", "header");


void Preview::run(sc::PreviewReplyProxy const& reply) {
    // Support three different column layouts
    // Client can display Previews differently depending on the context
    // By creates two layouts (one with one column, one with two) and then
    // adding widgets to them differently, Unity can pick the layout the
    // scope developer thinks is best for the mode
    ColumnLayout layout1col(1), layout2col(2);

    // add columns and widgets (by id) to layouts.
    // The single column layout gets one column and all widets
    layout1col.add_column({"headerId", "artId", "infoId", "telId", "actionsId"});
    // The two column layout gets two columns.
    // The first column gets the art and header widgets (by id)
    layout2col.add_column({"artId", "headerId"});
    // The second column gets the info and actions widgets
    layout2col.add_column({"infoId", "telId", "actionsId"});

    // Push the layouts into the PreviewReplyProxy intance, thus making them
    // available for use in Preview diplay
    reply->register_layout({layout1col, layout2col});

    //Create some widgets
    // header type first. note 'headerId' used in layouts
    // second field ('header) is a standard preview widget type
    PreviewWidget w_header("headerId", "header");
    // This maps the title field of the header widget (first param)  to the
    // title field in the result to be displayed in this preview, thus providing
    // the result-specific data to the preview for display
    w_header.add_attribute_mapping("title", "title");

    // Standard subtitle field here gets our 'artist' key value
    // w_header.add_attribute_mapping("subtitle", "artist");

    PreviewWidget w_art("artId", "image");
    w_art.add_attribute_mapping("source", "art");

    PreviewWidget w_info("infoId", "text");
    w_info.add_attribute_mapping("text", "address");

    PreviewWidget w_tel("telId", "text");
    w_tel.add_attribute_mapping("text", "telephone");

    Result result = PreviewQueryBase::result();
    QString urlString(result["uri"].get_string().c_str());
    // qDebug() << "[Details] GET " << urlString;
   // QUrl url = QUrl(urlString);

    // Create an Open button and provide the URI to open for this preview result
    PreviewWidget w_actions("actionsId", "actions");
    VariantBuilder builder;
            {"id", Variant("open")},
            {"label", Variant("Open")},
            {"uri", Variant(urlString.toStdString())} // uri set, this action will be handled by the Dash
    w_actions.add_attribute_value("actions", builder.end());

    // Bundle out widgets as required into a PreviewWidgetList
    PreviewWidgetList widgets({w_header, w_art, w_info, w_tel, w_actions});
    // And push them to the PreviewReplyProxy as needed for use in the preview


using namespace unity::scopes;
#include <QDebug>




我们使用热键Ctrl + R或按下绿色的按钮:



bzr branch lp:~liu-xiao-guo/debiantrial/dianpianclient3







DisplayName = Dianping Scope
Description = This is a Dianping scope
Art = screenshot.png
Author = Firstname Lastname
Icon = icon.png

PageHeader.Logo = logo.png
PageHeader.Background = color:///#FFFFFF
PageHeader.ForegroundColor = #F8500F
PageHeader.DividerColor = #F8500F
BackgroundColor = #FFFFFF
PreviewButtonColor = #F8500F



更多关于个性化的设置,可以参阅文章“Customization & branding”。到目前位置的所有源代码在如下的地址可以下载:

bzr branch lp:~liu-xiao-guo/debiantrial/dianpianclient5

定制Category template


//Create a JSON string to be used tro create a category renderer - uses grid layout
std::string CR_GRID = R"(
        "schema-version" : 1,
        "template" : {
        "category-layout" : "grid",
        "card-size": "small"
        "components" : {
        "title" : "title",
        "art" : {
        "field": "art",
        "aspect-ratio": 1.6,
        "fill-mode": "fit"


std::string CR_GRID = R"(
        "schema-version" : 1,
        "template" : {
            "category-layout" : "vertical-journal",
            "card-layout": "horizontal",
            "card-size": "small",
            "collapsed-rows": 0
        "components" : {
            "title" : "title",
                "field": "art2",
                "aspect-ratio": 1

同时,为了显示更多的信息,我们加入了“subtitle”及“summary”的项目到“Components”中。我们可以参考文章“Customization & branding”进行对比:



    struct Data {
        std::string name;
        std::string business_url;
        std::string s_photo_url;
        std::string photo_url;
        std::string rating_s_img_url;
        std::string address;
        std::string telephone;
        std::string summary; //added


        QVariantList deals = item["deals"].toList();

        QString summary;
        if ( deals.count() > 0 ) {
            QVariantMap temp = deals.first().toMap();
            summary = temp["description"].toString();

        qDebug() << "summary: " << summary;


            catres["subtitle"] = data.address;
            catres["summary"] = data.summary;
            catres["fulldesc"] = data.summary;
            catres["art2"] = data.s_photo_url;




bzr branch lp:~liu-xiao-guo/debiantrial/dianpianclient6


我们在这里想对dianping Scope做一个设置。比如我想有更多的搜寻的结果,而不是每次只有最多20个。我们可以通过文章“如何在Ubuntu Scope中定义设置变量并读取”来多我们的limit进行设置。首先,在Query类中加入函数。我们也需要在query.h文件中加入相应的定义:


    void initScope();

// The followoing function is used to retrieve the settings for the scope
void Query::initScope()
    unity::scopes::VariantMap config = settings();  // The settings method is provided by the base class
//    if (config.empty())
//        qDebug() << "CONFIG EMPTY!";

    int limit = config["limit"].get_double();
    cerr << "limit: " << limit << endl;



void Client::setLimit(int limit)
    m_limit = limit;


QString Client::getQueryString(QString query) {
    QMap<QString, QString> map;

    map["category"] = "美食";
    map["city"] = query;
    map["sort"] = "2";
    map["limit"] = QString::number(m_limit);  // 修改这个
    map["platform"] = "2";


void Query::run(sc::SearchReplyProxy const& reply) {

    // Initialize the scopes



type = number
defaultValue = 20
displayName = 搜寻条数



  FILES "${CMAKE_BINARY_DIR}/src/com.ubuntu.developer.liu-xiao-guo.dianping_dianping-settings.ini"

这个部分的改动是为了能够把设置文件也安装到手机的文件系统中去。我们可以在项目中点击右键,并选择运行“Run CMake”,这样,我们在Project中可以看到新添加的.ini文件。重新运行我们的Scope,并在Scope的右上角的设置图标(像有锯齿的 )去尝试改变limit的值,看看效果是什么样的。



bzr branch lp:~liu-xiao-guo/debiantrial/dianpianclient7






class Query: public unity::scopes::SearchQueryBase {
    Query(const unity::scopes::CannedQuery &query,
          const unity::scopes::SearchMetadata &metadata, api::Config::Ptr config);
    ~Query() = default;
    void cancelled() override;
    void run(const unity::scopes::SearchReplyProxy &reply) override;

    void initScope();

    api::Client client_;

    QString m_longitude;    // added
    QString m_latitude;  // added


const QString DEFAULT_LATITUDE = "39.9698";
const QString DEFAULT_LONGITUDE = "116.446";


void Query::run(sc::SearchReplyProxy const& reply) {
    try {

        // Get the current location of the search
        auto metadata = search_metadata();
        if ( metadata.has_location() ) {
            qDebug() << "Location is supported!";
            auto location = metadata.location();

            if ( location.has_altitude()) {
                cerr << "altitude: " << location.altitude() << endl;
                cerr << "longitude: " << location.longitude() << endl;
                cerr << "latitude: " << location.latitude() << endl;
                auto latitude = std::to_string(location.latitude());
                auto longitude = std::to_string(location.longitude());
                m_longitude = QString::fromStdString(longitude);
                m_latitude = QString::fromStdString(latitude);

            if ( m_longitude.isEmpty() ) {
                m_longitude = DEFAULT_LONGITUDE;
            if ( m_latitude.isEmpty() ) {
                m_latitude = DEFAULT_LATITUDE;

            qDebug() << "m_longitude1: " << m_longitude;
            qDebug() << "m_latitude1: " << m_latitude;
        } else {
            qDebug() << "Location is not supported!";
            m_longitude = DEFAULT_LONGITUDE;
            m_latitude = DEFAULT_LATITUDE;

        client_.setCoordinate(m_longitude, m_latitude);

        // Start by getting information about the query
        const sc::CannedQuery &query(sc::SearchQueryBase::query());
        QString queryString = QString::fromStdString(query.query_string());

        // Trim the query string of whitespace
        string query_string = alg::trim_copy(query.query_string());

        Client::DataList datalist;
        if (query_string.empty()) {
            queryString = QString("美食");  // changed
            datalist = client_.getData(queryString.toStdString());
        } else {
            // otherwise, get the forecast for the search string
            datalist = client_.getData(query_string);




#include <unity/scopes/SearchMetadata.h>
#include <QDebug>


    QString m_longitude;
    QString m_latitude;


void Client::setCoordinate(QString longitude, QString latitude)
    m_longitude = longitude;
    m_latitude = latitude;


QString Client::getQueryString(QString query) {
    QMap<QString, QString> map;

    map["category"] = query; //This is the new search item
    // map["city"] = query;
    map["sort"] = "2";
    map["limit"] = QString::number(m_limit);
    map["platform"] = "2";

    map["latitude"] = m_latitude;
    map["longitude"] = m_longitude;

这里我们对“category”进行搜索而不是上面的对“城市”进行搜索。经过这样的改造,我们基本上完成了我们对位置的搜索的Scope了。由于在Desktop上显示对位置不支持,所要对这个对位置的搜索进行测试,我们必须在真实的手机上进行测试。重新编译我们的Scope,并部署到一个真实的手机上。当我们重新运行我们的Scope在手机上时,在“Application Output”窗口显示的是如下的信息“

它显示我们并没有对位置进行支持虽然我们的GPS已经被打开了,这是什么原因呢?这是由于Ubuntu平台的安全性所造成的。我们必须修改我们的相关设置。我们可以参照文章“怎么在Ubuntu Scope中获取location地址信息”对我们的Scope进行配置。打开我们Scope的“com.ubuntu.developer.liu-xiao-guo.dianping_dianping.ini”,并加入如下的句子:

DisplayName = Dianping Scope
Description = This is a Dianping scope
Art = screenshot.png
Author = Firstname Lastname
Icon = icon.png

LocationDataNeeded=true // added

PageHeader.Logo = logo.png
PageHeader.Background = color:///#FFFFFF
PageHeader.ForegroundColor = #F8500F
PageHeader.DividerColor = #F8500F
BackgroundColor = #FFFFFF
PreviewButtonColor = #F8500F





一个更为完整的department的Scope可以在“在Ubuntu OS上创建一个department 点评Scope (Qt XML)”找到。到目前位置的所有的源码在如下的地址找到:

bzr branch lp:~liu-xiao-guo/debiantrial/dianpianclient8








作者:UbuntuTouch 发表于2014-11-14 13:15:58 原文链接
阅读:205 评论:0 查看评论

Read more
Steph Wilson

Community members at the Sprint

Victor and Andrew are two inspiring Community developers that have devoted their spare time to contribute to the Ubuntu Touch Music App team. I sat down with them during the Washington Device Sprint in October where they told us how they drew inspiration from the Design Team, and what drives them to contribute to Ubuntu.

You can read more about Victor and Andrew through their blogs, where they post interesting articles on their work and personal projects.

From left to right: Riccardo, Andrew, Filippo and Victor

From left to right: Riccardo, Andrew, Filippo and Victor

Hey guys, so when did you first get involved with Ubuntu?

Victor: “I started to contribute to the Ubuntu platform in March/April 2013 where I noticed there was no music app, so I started putting one together. It was pretty sketchy to start with, but it worked. I didn’t have a device to test it on so I mostly tested it using the platform on my desktop – so things were a bit hit and miss.

There was also another developer doing a music app, and at the time there was no core capability of playing music through an application for the proposed devices. Michael Hall (Open Source Software Developer) and Alan Pope (Engineering Manager) pulled Daniel Holm and I together, where we merged our core bases and started the music core app.

We didn’t have as much time as other applications, so we more or less sprinted like we are now to get things done. It was very spec driven and specific, which was helpful but sometimes it was hard to put together a full vision of what the designers wanted. So now we are redoing it from the feedback we have gathered, and it’s going pretty well. A little more agile than it was previously as to do thing faster, but it’s been fun the whole time. It’s nice to work on an application that people need and gets visibility, never get sick of hacking at it.”

Andrew: “I’m from North London, where I’m currently studying Software Engineering at Oxford Brookes University. I was working on my own music app where I just taught myself how to do things using my own framework, then I saw that these guys at Ubuntu had a similar problem to me, and so I thought I’d provide a patch. This then built up from there, and now here I am!”

Steph: “It’s amazing that someone can be in their bedroom writing codes and then suddenly your app is on a phone!”

Victor: “The other great thing about it is the Community Managers make it easy and apparent that you can contribute to different projects.”

Andrew: “Yeah someone just got in contact with me and asked me if I wanted to join the team and told me how open source projects work.”

What inspired you to contribute?

Victor: “A lot of my original inspiration was from what the Design Team had previously done. The previous iteration design spec was very large for the music app and it wasn’t as future driven, more just visually pleasing.”

Do you find it hard to implement some designs?

Victor: “We try to make it as close to the designs as we can, but obviously there’s compromises. There was some very flow driven things such as: sized cover arts that were hard to implement, but we can implement them now. It’s nice because they use the same pattern from other applications.”

Andrew: “Usually we just tell the designer that this is just not possible.”

What is it about open source that you like?

Victor: “I have been a user since 2006, but I have never been a large open source developer myself. It is hard to get involved with when you don’t know what you want to contribute to.”

Andrew: “Most applications are so developed already, so you would have to learn the existing code base and develop on it, whereas if you start a new you know everything from the get-go. Seeing your application on the device and knowing it can be on other devices too, is pretty exciting!”

How does it fit into your lifestyles?

Victor: “I’m a software engineer as well, so I write a lot of code. I haven’t really done QML or QT until I started doing these applications with the Ubuntu platform, so it has been a learning experience. I am learning something new from experienced people.”

Have you made any other applications for Ubuntu?

Victor: I’ve made a few games like Piano Tiles, and another that’s kind of like a clone of that but in QML – It’s a simple app but a good time waster haha.”

How much time does it take you to develop an app?

Victor: “It took me like a day. Andrew made a game last night! In 2 hours…”

Andrew: “Yeah we did! Loads of us at the sprint just got together in a room and made a few games.”

So you’re used to working remotely, does that put a barrier against things?

Andrew: “It sometimes delay things. However, you start to build this image of a person, so when you actually get to meet them you start to understand how they are and what makes them tick.

Victor: “Depends on how personal it really needs to be. If you are collaborating together and it’s mostly writing code and coming up with ideas, it doesn’t necessarily need to be face-to-face. It is obviously nicer, but you also get the benefit if the other person is a night owl in a different country where sometimes our hours overlap, two different chunks of time we’re working in.

Andrew: “There’s usually someone on IRC to speak to, it’s like a 24 hour operation haha.”

What’s the vibe like in the Community at the moment?

Victor: “It’s a pretty small Community at the moment, with close ties. Everyone is receptive to feedback, so if it was larger Community I don’t think it would be as receptive.”

Steph: “Thanks for your time guys!”

Here’s a sneaky preview of the music app, more will be revealed soon:

Album detail

Landing page

Read more
David Planella

.tg {border-collapse:collapse;border-spacing:0;border-color:#ccc;margin:20px auto;width:680px !important;} .tg td{font-size:14px;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:#ccc;color:#333;background-color:#fff;} .tg th{font-size:14px;font-weight:normal;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:#ccc;color:#333;background-color:#f0f0f0;} .tg .tg-4eph{background-color:#f9f9f9}


This is a week full of exciting events in the Ubuntu world! Following on the series of Ubuntu Scopes Workshops for the Ubuntu Scopes Showdown, we’re thrilled to announce more Scopes Workshops sessions as part of the Ubuntu Online Summit.

Scopes workshops: learn more and ask your questions

In order to support participants of the Scope Showdown, we’re organizing a series of workshops around different topics on writing scopes. These will be 1 hour hands-on sessions where the presenter will be demonstrating the topic live on video, with real code and using the Ubuntu SDK.

These are also meant to be interactive, so during and after the session the presenter will be answering the questions posted in real time by developers on the chat widget on each session page. Here’s the schedule for the workshops:

Workshop Time Presenter
Online Accounts for Scopes Developers Thursday, 13th Nov at 14:00UTC Alberto Mardegan
Scope Development How-Tos Thursday, 13th Nov at 15:00UTC Thomas Strehl & the Unity API Team

In a nutshell:

  • WHAT: Scopes workshops at the Ubuntu Online Summit
  • WHEN: Thursday, 13th November, starting at 14:00 UTC
  • WHERE: At the Ubuntu Online Summit

Looking forward to seeing you there!

Read more
David Callé

Departments are a way for the user to navigate the data source exposed by a scope. A music scope can use them to allow browsing by genre, a Youtube scope could list channels and playlists, a news scope could use them for listing topics, etc. Departments can also display a full hierarchy of sub-departments.

In this tutorial, you are going to learn how to create and add them to your scope.


scope_dep8 scope_dep9

Read more