Mapping mouse position to angles (QtQuick example)

Hi there! This post is to share with you something I hacked these days.

The use case

Image you are writing a widget or a piece of an application interface and you want to rotate something based on a mouse click. For instance, you may have a widget that looks like a rotating knob, or a round analogue gauge.

For this post, let’s use a car dashboard speedometer. What I want here is to click somewhere on the gauge and have the needle rotated towards the current mouse position. Check this video of how it should work.

The problem

In Qt, rotating a widget is easy in the context of QtQuick or QGraphicsView, just define the “rotation” property to the desired angle and you are all set. The problem here is to find out what the desired angle is.

Here I want to find out which is the angle formed between a given mouse position and centre of the gauge, or the origin of the needle rotation. Getting the mouse position is easy in QtQuick, simply watch the “mouseX” and “mouseY” properties of a MouseArea. But what about converting that to an “angle” ?

In mathematical terms what I want is to convert the mouse position from linear coordinates (X,Y pair) to circular coordinates (angle, radius pair) with the centre of the gauge being the origin of my plane. But designers using QtQuick really don’t want to deal with that.

The solution

I made a Qt class meant to be used as a “model” or “helper” for both C++ and QtQuick applications. This class hides the trigonometry required for that conversion by exporting a set of properties, namely:

  • originX
  • originY
  • x
  • y
  • angle
  • radius

So how would we use it in our example?

  1. Set “originX” and “originY” to the centre of the gauge. That’s the origin of the needle, right?
  2. Set “x” and “y” to the current mouse position. In QML that would be “x: mouseArea.mouseX” and “y: mouseArea.mouseY”.
  3. Read the value of “angle“. In QML that would be something like  “needleRotation.angle: angleModel.angle”

Then QtQuick data binding system will handle everything else. Every time the mouse moves, the properties “x” and “y” will be updated, the model will then recalculate the values of “angle” and “radius” causing the needle to update itself.

Below you can see a diagram of that behaviour. Note that the opposite also works, that is, once I explicitly set an “angle” and “radius” in the model, the values of “x” and “y” are automatically updated.

Relationship between AngleModel and different coordinates

Relationship between AngleModel and different coordinates

The Source

To grab the code go to Qt Components Gitorious and check:

  • examples/clickable-dial
  • examples/angle-model

Hope you enjoy it. Comments are always welcome.

Foi lançado o Qt 4.6 !

Está disponível desde hoje cedo a versão 4.6.0 do Qt com diversas novidades, como diz o post oficial dos nossos amigos do Qt.

A nova versão está disponível para download em qt.nokia.com, em versões pré compiladas para Windows, MacOS e Symbian, além do código fonte (para todas as plataformas). Usuários Linux podem baixar e compilar o fonte ou então instalar usando os procedimentos normais de cada distribuição, tão logo estejam disponíveis os pacotes desta versão.

Entre as novidades dessa versão estão:

  • API de Animação (QAnimation)
  • Máquina de Estados (QStateMachine)
  • Novo layout (QGraphicsAnchorLayout)
  • Suporte para a plataforma Symbian, o que permite que o Qt rode em dezenas de milhões de novos aparelhos 🙂

A documentação atualizada com todas as novas classes está pronta também, ou pelo Qt Assistant ou online.

Outro fato marcante desta versão é o fato de ser a primeira após a abertura do repositório Git do Qt, e assim marca uma integração cada vez maior com a comunidade. Nesse sentido esperamos que todos usem, testem e mandem opiniões e reports de eventuais bugs, para que as próximas versões sejam cada vez melhores!

Para ilustrar um pouco, um vídeo feito pelo pessoal aqui do openBossa mostrando o 4.6.0 rodando em diversos aparelhos!

Para finalizar, algumas fotos do time do Qt.

Em Oslo

Oslo Team

Em Berlin

Berlin Development Team

Em Brisbane

Brisbane Development Team

Em Munich

Munich Development Team

Parabéns pessoal!

O QGraphicsAnchorLayout

No último artigo mencionei que o QGraphicsLayout, a ser lançado no Qt 4.6, já está disponível no repositório público do Qt.

Hoje gostaria de dar uma visão geral sobre como este novo layout funciona e sobre o porquê de estarmos tão animados com isso.

Para tal, vou falar um pouco sobre: os layouts que já existiam no Qt, o uso do Anchor Layout e finalmente, os problemas que são melhores resolvidos por ele. De agora em diante assuma que estou escrevendo sobre o universo QGraphicsView e assim, onde ler “widget” ou “layout”, entenda que me refiro a QGraphicsWidgets ou QGraphicsLayouts.

QGraphicsLayouts existentes

Até o Qt 4.5, estavam disponíveis os layouts Linear e de Grid. Com eles, como seus nomes indicam, usuários poderiam ter seus widgets alinhados lado a lado ou em formato de tabela, com widgets ocupando uma ou mais células.

No entanto, não era possível organizar os itens para que ficassem fora da tela ou que se sobrepusessem, total ou parcialmente. Além disto, para obter o resultado desejado em alguns casos, era necessário utilizar diversos layouts encadeados.

O conceito do Anchor Layout

Layouts anteriores funcionavam assim: o layout Linear pedia que você adicionasse items ao layout em uma determinada seqüência e assim os itens seriam arranjados lado a lado (ou um sobre o outro); no caso do layout de Grid o conceito era similar, mas com itens associados a índices X,Y para representar a linha e coluna onde eles deveriam ser inseridos.

O Anchor Layout quebra com este paradigma no sentido de que não existe um conceito pré-definido de como os itens devem ser dispostos. A posição final de cada item é totalmente dependente de como você cria as âncoras. Em outras palavras, o Anchor Layout é um layout muito, muito flexível.

Apesar da maioria dos layouts serem flexíveis até certo ponto, e permitirem que se configure parâmetros como o espaçamento entre itens ou as margens, o Anchor Layout eleva a flexibilidade a um novo patamar. Foi também um dos nossos objetivos, fazer com que o processo de criação do layout fosse algo simples. Não queríamos expor centenas de parâmetros abstratos ao usuário, ao invés disto, nós  queríamos disponibilizar uma conceito e uma API de alto-nível. Além de fazer com que seu uso seja mais gratificante, isto facilita a tarefa de transformar em código C++, as idéias e a maneira de pensar dos designers de interfaces. E é assim que surgiram as âncoras.

Neste layout, cada item possui seis pontos de ancoragem, que são esquerda, direita, topo, base e os centros horizontal e vertical. O usuário por sua vez, é responsável por conectar os pontos de ancoragem de diferentes itens entre eles, e com os pontos de ancoragem do próprio layout. Essas conexões são feitas através das tais âncoras, que têm tamanhos bem definidos.

Depois, com base em:

– as âncoras criadas,
– os tamanhos mínimo, recomendado e máximo de cada item, e
– o tamanho do layout propriamente dito.

o layout calcula as geometrias apropriadas para cada um dos widgets.

Casos de uso do Anchor Layout no mundo real

Aqui no openBossa nós temos trabalhado no desenvolvimento de interfaces ricas há um bom tempo, e foi com essa experiência como bagagem que começamos a contribuir com o pessoal do Qt no desenvolvimento do QGraphicsAnchorLayout.

A seguir você irá encontrar alguns dos nossos casos de uso que não eram bem tratados pelos layouts existentes e que são bem mapeados para âncoras.

Transição entre telas

Nós freqüentemente precisamos fazer uma transição entre duas telas de uma mesma aplicação. Imagine que o usuário executa alguma ação que causar o sumiço da tela atual para que uma nova apareça.

Em uma aplicação de interface rica nós provavelmente gostaríamos de animar tal transição. Duas das alternativas seriam tornar transparente ou embaçar a tela corrente até que a que está por vir se faça visível. Para alcançar tal efeito, nós precisamos que as duas telas ocupem a mesma posição na tela, sendo que a mais velha deverá estar na frente (índice Z maior). Em seguida, basta aplicar o efeito à tela que está na frente, por exemplo mudar sua opacidade, e pronto, temos o efeito!

Então, como poderíamos usar um layout comum para fazer um widget (a tela nova) ocupar o mesmo espaço que outro (a tela velha)? Não dava. Agora com âncoras, basta ancorar os lados de uma tela aos lados da outra (direita com direita, topo com topo, etc) e pronto!

Usage of AnchorLayout to set two swallows on the same place

Uso do AnchorLayout para colocar duas telas no mesmo lugar.

Items fora da tela

Outro “truque” que usamos na animação de interfaces é fazer um item “entrar” ou “sair” da tela. Por exemplo, imagine uma barra de ferramentas no topo da tela que desliza pra dentro da tela e depois pra fora. A idéia aqui é criar dois estados (usando QStates talvez) onde a barra está dentro da tela em um deles,. e fora no outro.

Mais uma vez, com os layouts comuns não poderíamos criar o estado “fora-da-tela”. Com âncoras, basta ancorar a base da barra ao topo do layout, por conseqüência a barra será colocada logo acima do layout, pronta para entrar na tela quando necessário.

Usage of AnchorLayout to set a toolbar on or off-screen.

Uso do AnchorLayout para colocar um widget fora da tela.

Disposição orgânica

Existem também aqueles casos onde os designers criam um protótipo de tela com diversos itens arranjados de maneira disforme ou “orgânica”. Na maioria destes casos não é prático (ou possível) ter de usar diversos layouts encadeados para definir tal arranjo. No entanto, com um único Anchor Layout, todos os itens podem ser organizados de maneira precisa.

Experimente!

Existem outras situações não mencionadas onde um layout tão flexível também é útil. Eu acredito no entanto que os exemplos que coloquei aqui são suficientes para mostrar o tipo de problema que estamos tentando resolver. Por favor, experimente e nos diga o que achou. Todo o tipo de opinião é realmente bem vindo, quanto à forma de uso, performance, API, recursos e assim por diante. Tudo será levado em conta para que vocês tenham uma boa ferramenta nova para usar no Qt 4.6.

Aproveitem 🙂