Artigos‎ > ‎Qt‎ > ‎

QValidator

Validar Entrada do Usuário

Tratar erros de entrada do usuário costuma ser uma tarefa desgastante e que muitas vezes, ocupa grande parte do tempo de implementação de um software. Uma forma de reduzir o código para tratamento de erros é filtrar a entrada, não permitindo assim que o usuário consiga entrar com um valor inválido. Por exemplo: Para um campo de entrada QLineEdit que só pode receber um valor numérico inteiro, temos que impedir que o usuário possa digitar qualquer caractere que não seja um número no intervalo de (0...9) ou um sinal de menos (-).
Para isso, poderíamos reimplementar o evento KeyPress para impedir a entrada de caracteres inválidos. Mas somente evitar que sejam digitados caracteres inválidos, muitas vezes não é o suficiente para evitar entradas inválidas. Por exemplo: O usuário poderia digitar 92-1 o que não corresponde a um número inteiro válido, pois o sinal de menos só poderia aparecer na primeira posição da string. Para eliminar esse problema poderíamos fazer uma rotina de validação que é executada quando o texto está sendo editado. Quando estamos implementando um software que contem muitos campos que devem ser filtrados, o trabalho de filtragem se torna muito cansativo e o código muito grande. Em muitos casos podemos usar máscaras de entrada, mas às vezes elas são limitadas ou não aplicáveis.
Para resolver esse problema e simplificar nosso trabalho de validação da entrada, existe no Qt a classe abstrata QValidator que serve para validar entradas de texto. Desta se derivam três subclasses, que são:
  • QIntValidator – Valida a entrada de números inteiros.
  • QDoubleValidator – Valida a entrada de números fracionários.
  • QRegExpValidator – Valida a entrada com uma expressão regular personalizada.


Se quisermos que em um determinado QLineEdit o usuário só possa digitar um valor numérico inteiro poderíamos usar o QIntValidator desta forma:

  QIntValidator *intValidator = new QIntValidator(this);
  QLineEdit *lineEdit = new QLineEdit(this);
  lineEdit->setValidator(intValidator);

Vamos supor que estamos desenvolvendo um aplicativo de controle de vendas. Quando o vendedor selecionar algum produto, uma variável chamada QuantDisponivel irá indicar quantas unidades do produto estão disponíveis no estoque.
Queremos que o vendedor entre com a quantidade do produto a ser vendida em um QLineEdit. Mas precisamos evitar que ele digite algum valor que não seja um número inteiro. Também queremos evitar que o vendedor digite um valor acima da quantidade disponível em estoque. Isso poderia ser implementado setando o range de QIntValidator desta forma:

   QIntValidator *intValidator = new QIntValidator(0, QuantDisponivel, this);

Assim, estamos passando o range logo no construtor. Podemos também modificar o range utilizando as funções setRange, setButton ou setTop.

Um recurso fantástico para a validação de entrada é o  QRegExpValidator. Não vou entrar em detalhes sobre expressões regulares, mas um bom resumo pode ser encontrado em:
http://guia-er.sourceforge.net/index.html

Para demonstrar o QRegExpValidator, vamos supor que em um campo QLineEdit do nosso aplicativo, precisamos que o usuário digite um valor hexadecimal de 0 a FFFF. Para isso, podemos usar o QRegExpValidator assim:

    QValidator *hexValidator = new QRegExpValidator(QRegExp("[0-9A-Fa-f]{1,4}"), this);

Desta forma o usuário só poderá digitar números no intervalo de (0...9), letras minúsculas (a...f) e maiúsculas (A...F). Para a expressão ser válida ela deverá ter no mínimo um dígito e só poderão ser digitados no máximo 4 dígitos.
Com a expressão regular, podem ser feitos os mais diversos tipos de validadores de entrada personalizados. Como outro exemplo de expressão regular, esta serve para entrada de horas no formato (hh:mm) "([01][0-9]|2[0-3]):[0-5][0-9]".
Nossa entrada vai até 23h e 59m. Dessa forma se o primeiro dígito da hora for 0 ou 1, o segundo pode ir até nove, o que poderia ser (09h) ou (19h). Mas se o primeiro dígito for 2, o segundo só pode ir até 3 que seria o nosso máximo para hora (23h). Neste caso se utiliza o OU, que é representado pelo símbolo (|). Então, a hora pode ser:


Com o primeiro digito (0 ou 1) e o segundo de (0...9)
OU (|)
Com o primeiro dígito igual a dois e o segundo de (0...3)

Para os minutos, como vão até 59, ficaria assim:


O primeiro dígito pode ser de (0...5) e o segundo de (0...9)

Além de usarmos as subclasses QIntValidator, QDoubleValidator e QRegExpValidator, também podemos herdar a classe virtual QValidator e implementar nossa própria classe de validação. Uma vantagem disso é que podemos não somente validar a entrada como também fazer transformações no texto de entrada. Por exemplo, podemos validar a entrada e transformá-la em maiúscula.
Para demonstrar, vamos criar um validador de entrada hexadecimal que pode permitir entrada negativa (-FF) e pode também transformar a entrada para maiúscula.

Aqui está nosso arquivo de cabeçalho:

#ifndef FVALIDATORS_H
#define FVALIDATORS_H
#include <QValidator>
class FHexValidator : public QValidator
{
   Q_OBJECT
public:
   explicit FHexValidator(QObject *parent = 0, bool _Signal = false, bool _UpperCase = true);
   void setSignal(bool _Signal) { Signal = _Signal; }
   void setUpperCase(bool _UpperCase) { UpperCase = _UpperCase; }
   QValidator::State validate(QString &input, int &pos) const;
private:
   QRegExpValidator *validator;
   bool Signal;
   bool UpperCase;
};
#endif // FVALIDATORS_H

Criamos uma classe  FHexValidator que é herdada de  QValidator e reimplementamos a função virtual pura validate.
Estou usando QRegExpValidator para simplificar o trabalho de filtrar a entrada hexadecimal, mas uma rotina que faça isso poderia ser implementada facilmente.
A variável Signal indica se a entrada pode ser negativa e a variável UpperCase indica que a entrada deve ser convertida em maiúscula.

Vamos ao código:


#include "fvalidators.h"

FHexValidator::FHexValidator(
QObject *parent, bool _Signal, bool _UpperCase): QValidator(parent)
{
   validator = new QRegExpValidator(QRegExp("[-0-9A-Fa-f]{1,8}"), this);
   Signal = _Signal;
   UpperCase = _UpperCase;
}

QValidator::State FHexValidator::validate(QString &input, int &pos) const
{
   if(input.contains('-'))
   {
       if(!Signal || input[0] != '-' || input.count('-') > 1) return Invalid;
if(input.size() < 2) return Intermediate;
   }
   if(UpperCase) input = input.toUpper();
   return validator->validate(input, pos);
}


No construtor iniciamos validator para validar uma entrada hexadecimal com até 8 dígitos que pode conter o sinal de (-).

A implementação de validate é bem simples. Ele pode retornar 3 estados, que são:

  • Invalid – Quando a entrada é inválida.
  • Intermediate – Quando ainda não é determinado se a entrada é válida.
  • Acceptable – A entrada é válida.


Primeiro a função checa se a string de entrada contém o caractere (-). Caso contenha a entrada pode ser inválida se o modo de entrada negativa (Signal) não estiver habilitado ou se o caractere (-) não estiver na primeira posição da string. Somente com essas condições poderia acontecer um erro se o usuário digitar (-), voltar o cursor para a primeira posição e digitar novamente (-). Para isso não acontecer a entrada também será invalidada se a string contiver mais de um caractere (-).
Se a string contém o sinal (-) e não foi invalidada, precisamos saber se existem mais dígitos, pois uma string que contém somente o sinal (-) não é aceitável, mas sim indeterminada. Pois necessita de algum dígito para ser válida.

Logo após, se o modo  UpperCase estiver ativo, o texto é transformado em maiúsculo.

Finalmente a string pode ser validada pelo objeto validator, que vai verificar se nela existem somente dígitos hexadecimais e o sinal (-).

É isso aí pessoal, vou ficando por aqui e espero que tenham gostado do artigo.
Até a próxima!

Comments