terça-feira, 18 de janeiro de 2011

Alterando a cor das células de uma QTableView no Qt 4.7

Quando precisei achar alguma informação sobre como alterar a cor de uma célula em uma QTableView, fiquei surpreso com a falta de material "completo". Existem muitos posts pela Web sobre como realizar esta tarefa de diferentes maneiras, mas sem mostrar a solução na sua totalidade. Neste post, espero poder contribuir de maneira efetiva para a solução deste problema.

Ao criar uma aplicação para exibir registros de log de um firewall utilizando o framework Qt 4.7, tive a necessidade de utilizar uma QTableView para exibir os registros extraídos de um SGBD PostgreSQL. O objetivo era utilizar o tag (também podemos chamar de rótulo) de cada registro para definir a cor das células da QTableView utilizadas para exibí-lo. A figura abaixo mostra uma parte destes registros exibidos em uma QTableView, com uma cor por registro (ou uma cor para vários tipos de registros).


Na figura acima, os tags são mostrados na terceira coluna, após o campo "Hora". Dependendo do valor do registro contido neste campo, será definida uma cor para todas as células que compõe a linha (row).

O primeiro problema foi descobrir como se altera a cor de uma linha ou coluna de uma QTableView. A documentação do site não explica diretamente como realizar esta tarefa, mesmo no extenso artigo intitulado "Model/View Programming". Então, decidi pesquisar a API do Qt para encontrar os métodos (ou funções, como queiram) responsáveis por esta tarefa. Apesar dos exemplos, também não foi possível encontrar uma solução completa.

Aqui vale uma nota: "encontrar uma solução completa" não significa encontrar uma resposta pronta, do tipo "copiar e colar", mas de uma orientação ou exemplo que fosse objetivo, mostrando como esta tarefa poderia ser realizada. Por exemplo, ao ser deparado com um problema semelhante em Java ou C#, a informação foi encontrada mais rapidamente, e a solução foi mais direta.

No final das contas, existem duas soluções "aconselháveis" para resolver este problema: criar uma subclasse da QTableView ou uma subclasse do QItemDelegate (responsável por renderizar as células de uma QTableView) e reimplementar um determinado método. Optei por criar uma subclasse do QItemDelegate e reimplementar o método paint, e esta será a solução demonstrada aqui.

Em primeiro lugar, é necessário criar uma subclasse do QItemDelegate (chamada "ColorDelegate" no exemplo), começando pelo header:


A seguir, a implementação:

Outra nota: a intenção deste post não é ensinar melhores práticas de engenharia de software ou ensinar como programar em C++ utilizando o framework Qt ou sequer afirmar que esta é a única (ou a melhor) solução para este problema, mas simplesmente mostrar de uma maneira direta e completa uma das soluções possíveis. Sendo assim, o código aqui apresentado é uma simplificação do código de produção, a fim de torná-lo mais objetivo.

Antes de poder alterar a cor de um conjunto de células, seria necessário buscar o tag na sua devida célula. Acontece que o método paint do delegate é utilizado pela QTableView para renderizar cada uma de suas células. Se o tag está inserido na terceira coluna de uma linha, como é possível alterar a cor das células já processadas, da primeira e segunda colunas? Este era o segundo problema.

A solução está na linha 16 da implementação: é necessário buscar o modelo que está alimentando a QTableView, e então buscar os dados deste modelo. Uma vez que temos acesso aos dados do modelo, basta buscar o registro desejado através do seu índice. O método index() do modelo necessita de dois parâmetros: linha (row) e coluna (column). A "linha" é a linha do modelo que está sendo processada pelo delegate – acessada através do método row() do QModelIndex fornecido como parâmetro – e a "coluna" é a de número dois (a primeira coluna é a de número zero).

Uma vez que o tag é encontrado, pode ser utilizado para decidir qual cor aplicar às células. A partir da linha 19 temos uma série de if ... else if que atribuem a cor das células baseados no valor do tag. É importante notar que na comparação de igualdade do tag com as strings pré-definidas (linhas 19, 22 e 24) é uma boa prática usar a classe QLatin1String de forma a evitar hidden mallocs, conforme demonstrado neste site.

Na linha 29 é decidido se a cor do texto exibido na célula deve ser invertida – isto é, definida como branca ao invés de preta – para torná-la mais legível. Dependendo da cor aplicada ao tag será necessário inverter a cor da fonte.

Na linha 34 é definido o alinhamento do texto dentro da célula – neste caso, o texto é alinhado ao centro, tanto horizontalmente (Qt::AlignCenter) quanto verticalmente (Qt::AlignVCenter).

Na linha 37 a célula é efetivamente preenchida com a cor definida, e na linha 40 o método paint da classe pai (ou superclasse) é invocado para continuar a renderização da célula.

Também vale notar que as operações de alteração de cor e alinhamento do texto da célula são feitas sobre uma cópia do objeto QStyleOptionViewItem informado como parâmetro, uma vez que ele é const e não permite alterações.

Após ter criado a classe ColorDelegate, basta atribuí-la à um objeto QTableView previamente criado. Abaixo é demonstrado um exemplo simplificado deste processo:

Para concluir, gostaria de enfatizar que os exemplos aqui demonstrados também não são do tipo "copiar e colar". Entretanto, acredito que tudo que é necessário para permitir a customização na renderização das células de uma QTableView foi demonstrado.

Um comentário:

  1. Oi Roberto!
    Olha que interessante o teu blog.
    Não entendo muito dessas tecnologias, aliás, nada...rsrs...tenho um primo que sempre manda algumas informações por e-mail, mas repasso pro meu esposo que entende.

    Obrigado pela sua visita no meu blog.

    Temos um blog da classe dos adolescentes da igreja, se qualquer hora vc desejar escrever algo pra eles, seria muito legal. Lá eu e os professores da classe postamos a lição diária deles e outros textos diversos, também poesia e imagens. Nesses últimos 2 meses estamos postando poucas coisas diferentes, tá todo mundo em ritmo de férias...rsrs.
    Se um dia desejares contribuir, ficaremos felizes. Com certeza coloco o teu nome e o link aqui do teu blog pra eles conhecerem.

    O endereço do blog para conheceres é:
    http://portaladolescentes.blogspot.com

    Se perferir, podes entrar pelo meu blog, está na lista de blogs do lado direito, o nome é: coisa de adolescente.

    Vou terminando por aqui esse big comentário.
    Grande abraço e um ótimo dia.

    ResponderExcluir