Make your own free website on Tripod.com

Implementação das classes

Ao criar uma classe, são acrescentados ao projeto dois arquivos que têm o mesmo nome da classe, porém em minúsculo um com extensão .h e outro .cpp. Se, na janela de classes, for dado um clique sobre o nome da classe, o KDevelop abre uma janela mostrando o header e se o clique for sobre um dos métodos, o KDvelop abre o .cpp na posição onde está implementado o método.

Primeiramente, serão implmentadas as classes que acessam o banco de dados e depois, as classes de interface com o usuário, que fazem uso dessas classes. Depois de tudo codificado, será modificado o código da função main() para que ela chame a janela de cadastro de formas de pagamento ao invés da janelinha criada junto com o projeto.

Para cada classe, serão mostrados os fontes de cada método e será explicado o que ele está fazendo. Nessa explicação ficará explícito que o que se precisa de C++ é apenas o necessário para implementar os métodos. Nada de rotinas de baixo nível, ou chamadas complicadas de sistema, mas apenas o que as bibliotecas utilizadas exigem para funcionar.

Ao gerar os arquivos com os códigos fonte, tanto o Umbrello como o KDevelop geram várias linha de comentários no formato do DOxygen além de outras informações adicionais. Para mostrar a implementação das classes, todas as linhas adicionais foram removidas e assim, os fontes que serão mostrados contêm apenas as linhas válidas para o compilador.

Classes de acesso ao banco de dados

Antes de começar a implementar as classes de banco de dados, é necessário entender como o MySQL funciona. Basicamente a tarefa de obter dados do banco de dados consiste na seguinte seqüência de passos:

  1. Abrir uma conexão com o banco de dados;

  2. enviar uma instrução SQL;

  3. receber o conjunto de registros resultado da instrução SQL;

  4. obter os dados de cada registro do conjunto;

  5. desalocar o conjunto de registros;

  6. e finalmente, fechar a conexão com o banco de dados.

Para cada passo, deve existir uma variável para armazenar o resultado da operação realizada. Estas variáveis serão os atributos da classe. Portanto os atributos necessários para gerenciar a conexão com o MySQL são os seguintes:

conexão

Este atributo vai conter um ponteiro que será o ponto de comunicação entre o sistema e o banco de dados. Ele é do tipo MYSQL que é uma struct definida em mysql.h.

registros

Este atributo, será um ponteiro inicializado com NULL e será usado logo após executar uma instrução SQL do tipo SELECT para receber o conjunto de registros retornados pela instrução. O tipo deste atributo é MYSQL_RES, outra struct definida em mysql.h.

registro

Este atributo vai conter os dados de um único registro do conjunto de regsitros registros. O tipo é MYSQL_ROW, também definido em mysql.h. Este atributo não deve ser um ponteiro.

Os detalhes da conexão com o banco de dados serão implementados na classe QDatabase, sendo que as outras classes devem apenas conhecer a linguagem SQL e usar os métodos implementados em QDatabase. Portanto, os atributos mostrados são da classe QDatabase e não serão usados diretamente, mas via métodos implementados nesta classe.

Todas as classes de banco de dados (QDatabase, QListaFormaPagto e DFormaPagto foram criadas pelo Umbrello, e portanto, a estrutura delas está pronta nos arquivos fontes gerado por ele. Assim, resta apenas o trabalho de alterar os arquivos fonte para implementar os métodos das classes.

Classe QDatabase

A classe QDatabase é a que vai conversar diretamente com a API do SGBD, ou seja, as chamadas às rotinas implementadas na biblioteca do MySQL, serão feitas por esta classe. As classes filhas de QDatabase não precisam saber conversar com o MySQL, pois QDatabase, vai cuidar dessa conexão.

A figura Figura 20 mostra a estrutura da classe QDatabase. É importante notar que ela é filha de QObject. Na verdade, não era necessário que ela fosse filha de QObject, mas isto foi feito por dois motivos. Um deles é que praticamente todas as classes da biblioteca Qt são filhas de QObject e seus construtores recebem um objeto desta classe ou de um de seus descendentes. Quando um objeto é destruído, ele destrói também os objetos que o receberam como filho. Se, por exemplo, for instanciada uma classe de interface e em seguida for instanciada outra classe recebendo a interface, quando a instaância da interface for destruída, automaticamente o segundo objeto também será destruído. Dessa forma não há o perigo de "esquecer" de destruir um objeto, pois a biblioteca Qt vai fazer isto automaticamente.

O segundo motivo pelo qual QDatabase é filha de QObject, é para ilustrar a utilização do mecanismo signal and slot do Qt, mais especificamente, a implementação de um sinal. Em QDatabase, sempre que ocorrer um erro na comunicação com o SGBD, um sinal de erro será emitido e que pode ser conectado a um slot.

    class QDatabase : public QObject
    {
        Q_OBJECT
    public:
  5     QDatabase(QObject *parent=0, const char *name=0);
        ~QDatabase(  );
    
        bool proximo(  );
        QString obtemCampo( QString campo );
 10     long numeroRegistros(  );
    
        bool executaSQL( QString sql );
        bool getResult(  );
    
 15 private:
        void initAttributes();
    
        MYSQL *conexao;
        MYSQL_RES *registros;
 20     MYSQL_ROW registro;
    
    signals:
        void erro(const char *);
    
 25 public slots:
        void msgErro(const char *strErro);
    };

Figura 20. Estrutura da classe QDatabase

A linha 3 da Figura 20, é uma macro da biblioteca Qt, necessária para toda classe descendente de QObject. O método initAttributes() foi criado pelo Umbrello para que os atributos da classe sejam inicializados com os seus valores padrão. A linha 24 declara um sinal para a classe, que será emitido todas as vezes que ocorrer um erro na conexão com o SGBD. É importante notar que este método não é implementado. A macro Q_OBJECT cuida da conversão desta declaração de sinal em um código que o compilador entende.

Por fim a linha 27 declara um slot que recebe um texto. Ele será usado nas classes filhas de QDatabase para exibir as mensagens de erro emitidas pela classe. Na verdade não é necessário que este slot específico seja usado, mas qualquer um que receba uma string implementado em qualquer classe. Este slot foi criado para ser o padrão. Para ilustrar este fato, a classe DListaFormaPagto usa este slot e a classe KFormaPagto conecta o sinal erro a um slot implementado em sua própria estrutura.

Depois de mostrar a estrutura da classe QDatabase, a partir de agora será mostrada a implementação de seus métodos. A Figura 21 implementa o construtor. A sua primeira tarefa é inicializar os atributos. Depois disto ele faz a conexão com o SGBD. As linhas 8 e 10 ilustram a utilização do sinal erro. Se um erro ocorre, o sinal é emitido.

    QDatabase::QDatabase(QObject *parent,
      const char *name) : QObject(parent, name){
    
      initAttributes();
  5   if ( (conexao = mysql_init(NULL)) )
        if (! (mysql_real_connect(conexao, host,
            usuario, senha, db, 0, NULL, 0)))
          emit erro(mysql_error(conexao));
      else
 10     emit erro(mysql_error(conexao));
    }

Figura 21. O construtor da classe QDatabase

O método initAttributes() mostrado na Figura 22 é um método criado pelo Umbrello para inicializar os atributos da classe com seus valores padrão. Esta é uma forma interessante de trabalho, principalmente se a classe tem mais de um construtor e os valores padrão dos atributos forem o mesmo, pois não é necessário repetir para cada construtor as atribuições, mas apenas chamar o método initAttributes().

void QDatabase::initAttributes( ){
  conexao = 0;
  registros = 0;
}

Figura 22. Implementação do método initAttributes()

A Figura 23 implementa o destrutor da classe. Ele primeiramente garante que o conjunto de registros será fechado e depois fecha a conexão com o SGBD.

QDatabase::~QDatabase(  ){

  if (registros) mysql_free_result(registros);
  mysql_close(conexao);

}

Figura 23. Destrutor de QDatabase

Na Figura 24 está implementado o método executaSQL, que é responsável pelo envio de instruções SQL ao SGBD. Ele recebe uma string contendo a instrução SQL e, se o SGBD conseguir executá-la, é retornado true, caso contrário o método retorna false. Também neste método, caso ocorra um erro, um sinal é emitido.

bool QDatabase::executaSQL( QString sql ){

  bool retorno;

  retorno = ( mysql_real_query(conexao, sql,
             sql.length()) == 0);
  if (!retorno) emit erro(mysql_error(conexao));
  return retorno;

}

Figura 24. Implementação do método executaSql()

Depois de executar uma instrução SQL do tipo SELECT, o primeiro passo é obter o conjunto de registros que o SGBD vai retornar. Isto é feito pelo método getResult(), mostrado na Figura 25. A primeira providência do método é confirmar se a conexão com o banco de dados está aberta e em seguida é verificado se o conjunto de registros está aberto. Se o conjunto de registros estiver aberto, ele é fechado antes. Depois dos testes, o resultado da instrução SQL executada é obtido do SGBD. Mais uma vez, se ocorrer algum erro, o sinal é emitido.

bool QDatabase::getResult(  ){

  if (!conexao) return false;

  if (registros) mysql_free_result(registros);
  registros = mysql_store_result(conexao);
  if (mysql_errno(conexao) != 0){
    emit erro(mysql_error(conexao));
    return false;
  }else
    return true;
}

Figura 25. Implementação do método getResult()

Depois de executar a instrução SQL e obter o conjunto de registros, deve-se usar o método proximo() mostrado na Figura 26. A cada chamada a este método, um registro é obtido do banco de dados. Quando o último registro for lido, na próxima leitura, a função mysql_fetch_row() vai retornar um valor NULL, fazendo com que o método retorne o valor false. Este valor de retorno pode ser usado como condição de parada na leitura dos registros do conjunto.

bool QDatabase::proximo(  ){
  return ((registro = mysql_fetch_row(registros)) ?
          true : false);
}

Figura 26. Implementação do método proximo()

Para obter os dados de cada registro, deve ser usado o método obtemCampo() mostrado na Figura 27. O método percorre a lista de campos que a instrução SQL retorna e ao encontrar o campo desejado, retorna o seu valor. Não é necessário conhecer a instrução executada para isso, pois o conjunto de registros tem todas as informações sobre os campos.

QString QDatabase::obtemCampo( QString campo ){

  unsigned int numCampos;
  unsigned int i;
  MYSQL_FIELD *campos;

  numCampos = mysql_num_fields(registros);
  campos = mysql_fetch_fields(registros);
  for(i = 0; (i < numCampos) &&
      (QString(campos[i].name) != campo); i++);
  if (i < numCampos)
    return QString(registro[i]);
  else
    return "";
}

Figura 27. Implementação do método obtemCampo()

Em algumas situações pode ser necessário saber o número total de registros que a instrução SQL retornou e isto é a tarefa do método numeroRegistros implementado como mostrado na Figura 28. Este método apenas retorna o valor da função mysql_num_rows(), implementada na biblioteca do MySQL.

long QDatabase::numeroRegistros(  ){
  return mysql_num_rows(registros);
}

Figura 28. Implementação do método numeroRegistros()

Finalmente, é mostrada na Figura 29 a implementação do método msgErro(). Este método é um slot de QDatabase que mostra na tela uma caixa de diálogo contendo um texto representando um erro que é passado como parâmetro. Ele foi implementado para ser o slot padrão para mostrar mensagens de erro do SGBD.

void QDatabase::msgErro(const char *strErro)
{
  QMessageBox::warning(0, "Banco de dados", strErro);
}

Figura 29. Implementação do slot msgErro()

Assim foi mostrada a implementação completa da classe QDatabase. Ficou evidente na implementação dos métodos que a conexão com o SGBD não é uma tarefa difícil. A grande vantagem de implementar o acesso à API do SGBD nessa classe é que, se ao invés do MySQL for usado outro SGBD como o PostgreSQL, apenas a classe QDatabase precisa ser alterada, pois as suas filhas apenas trabalham com instruções SQL que funcionam para qualquer SGBD.

Classe DListaFormaPagto

A Figura 30 mostra a estrutura da classe DListaFormaPagto. Na declaração da classe, é importante notar que ela é uma filha de QDatabase que implementa o acesso à API do MySQL. Ela contém apenas dois métodos: o construtor e obtemDados().

class DListaFormaPagto : public QDatabase
{
public:
    DListaFormaPagto(QObject *parent=0, const char *name=0);
    bool obtemDados(  );
};

Figura 30. Estrutura da classe DListaFormaPagto

A Figura 31 mostra a codificação do construtor DListaFormaPagto(). Ele é muito simples apenas conectando o sinal emitido quando um erro ocorre a um slot.

DListaFormaPagto::DListaFormaPagto(QObject *parent,
  const char *name) : QDatabase(parent, name)
{
  connect(this, SIGNAL(erro(const char *)), this,
    SLOT(msgErro(const char *)));
}

Figura 31. O construtor de DListaFormaPagto

A Figura 32 mostra a implementação do método obtemDados(). Este método define a instrução SQL que deve ser enviada ao SGBD para obter uma lista com as formas de pagamento cadastradas no sistema. Definida a instrução SQL, executa-se o método executaSQL(), e se ele executar com sucesso, é executado també o método getResult(), ambos definidos na classe QDatabase.

bool DListaFormaPagto::obtemDados(  ){

  QString sql = "SELECT CODIGO,DESCRICAO,PRAZO,PESOCOM,"
  sql += "PESODESC FROM FORMAPAGTO WHERE SITUACAO='A'";
  return ( (executaSQL(sql)) ? getResult() : false);

}

Figura 32. Implementação do método obtemDados()

A classe DFormaPagto

A classe DFormaPagto foi criada para manipular os dados de uma forma de pagamento. Para isso, ela tem, além dos atributos usados para conectar ao banco de dados, atributos que representam as colunas da tabela FORMAPAGTO. A Figura 33 mostra a sua estrutura. A classe tem dois construtores, sendo um chamado quando for para criar uma nova forma de pagamento e o outro, que recebe o código da forma de pagamento como parâmetro, chamado quando for para alterar uma forma de pagamento existente.

class DFormaPagto : public QDatabase
{

public:
    DFormaPagto(QObject *parent=0, const char *name=0);
    DFormaPagto(QString formaPagto, QObject *parent=0,
                const char *name=0);

    bool alteraCampo( QString campo, QString valor );
    bool obtemDados(  );
    bool grava(  );
    bool exclui(  );
    bool valida(  )

private:
    QString codigo;
    QString descricao;
    QString prazo;
    QString pesocom;
    QString pesodesc;
    QString situacao;

};

Figura 33. Estrutura da classe DFormaPagto

A Figura 34 mostra a implementação dos dois métodos da classe. o primeiro não faz nada a não ser, em seu cabeçalho, chamar o construtor de QDatabase. O segundo construtor atribui o valor do código da forma de pagamento que será alterada. Depois disso ele busca os seus dados no banco de dados, chamando os métodos obtemDados() e proximo().

DFormaPagto::DFormaPagto(QObject *parent,
  const char *name) : QDatabase(parent, name)
{
}

DFormaPagto::DFormaPagto(QString formaPagto,
  QObject *parent, const char *name)
  : QDatabase(parent, name)
{
  codigo = formaPagto;
  obtemDados();
  proximo();
}

Figura 34. Construtores da classe DFormaPagto

O método obtemDados() é implementado da mesma forma que em DListaFormaPagto alterando apenas a instrução SQL que é enviada ao banco de dados e por isto não será mostrado. A Figura 35 mostra a implementação do método alteraCampo(). Ele altera o valor de um dos atributos da classe. O atributo a ser alterado e seu valor são os parâmetros que o método recebe.

bool DFormaPagto::alteraCampo(QString campo,
  QString valor )
{

  if (campo == "DESCRICAO") descricao = valor;
  else if (campo == "PRAZO") prazo = valor;
  else if (campo == "PESOCOM") pesocom = valor;
  else if (campo == "PESODESC") pesodesc = valor;
  else return false;

  return true;

}

Figura 35. Implementação do método alteraCampo()

O próximo método, mostrado pela Figura 36, é o grava(), que grava os dados da forma de pagamento no banco de dados. Este método deve ser chamado sempre depois de alteraCampo() que atualiza os atributos da classe com os dados da forma de pagamento que devem ser enviados ao banco de dados. Antes de gravar os dados, o método valida() é chamado para verificar se os dados da forma de pagamento são válidos.

Depois de validados os dados, é feito um teste no atributo codigo para ver se ele tem algum valor. Se estiver vazio, significa que uma nova forma de pagamento está sendo criada. Caso contrário, uma forma de pagamento está sendo alterada. Para o primeiro caso é elaborada uma instrução INSERT e no segundo uma instrução UPDATE.

bool DFormaPagto::grava(  )
{
  QString sql;

  if (valida()){
    if (codigo.isEmpty()){
      sql = "INSERT INTO FORMAPAGTO (DESCRICAO,PRAZO,"
      sql += "PESOCOM,PESODESC) VALUES ('"
      sql += descricao + "','" + prazo + "'," +
             pesocom + "," + pesodesc + ")";
    else
      sql = "UPDATE FORMAPAGTO SET DESCRICAO='" +
            descricao + "',PRAZO='" + prazo + "',"
            + "PESOCOM=" + pesocom + ",PESODESC=" +
            pesodesc + " WHERE CODIGO=" + codigo;
    return executaSQL(sql);
  }else return false;

}

Figura 36. Implementação do método grava()

O método exclui(), mostrado na Figura 37, faz a exclusão de uma forma de pagamento. O comportamento da classe depende de ela ter sido usada em uma venda. Se a forma de pagamento ela já foi usada alguma vez, ela não é excluída, mas apenas marcada como inativa e se nunca foi utilizada, ela é excluída do sistema.

bool DFormaPagto::exclui(  ){

  QString sql;
  QDatabase dbVerifica;

  sql = "SELECT FORMAPAGTO FROM PAGAMENTO WHERE FORMAPAGTO="
        + codigo
  if (dbVerifica.executaSQL(sql) && dbVerifica.getResult()){
    if (dbVerifica.numeroRegistros() > 0)
      sql = QString("UPDATE FORMAPAGTO SET SITUACAO='I' "
            + "WHERE CODIGO=" + codigo;
    else
      sql = "DELETE FROM FORMAPAGTO WHERE CODIGO="
            + codigo;
    return executaSQL(sql);
  }else return false;

}

Figura 37. Implementação do método exclui()

Finalmente, para a classe DFormaPagto, é mostrado o método valida(), implementado como na Figura 38. Ele é muito simples porque a validação da forma de pagamento consiste em apenas verificar o preenchimento ou não de seus campos. Para fazer a verificação, é usada uma única expressão lóca que retorna true se os dados são válidos.

bool DFormaPagto::valida()
{
  return (!(descricao.isEmpty() && pesocom.isEmpty()
          && pesodesc.isEmpty()) &&
          ( (prazo=="S") || prazo=="N") );
}

Figura 38. Implementação do método valida()

Classes de interface com o usuário

Depois de implementadas as classes que vão fazer a comunicação com o SGBD, são mostradas as classes que possibilitam ao usuário interagir com o sistema. Todas as classes mostradas a partir de agora são filhas de uma classe definida pela biblioteca Qt. Mais especificamente, a classe KListaFormaPagto é descendente de QWidget e a classe KFormaPagto é descendente de QDialog. Porém elas não são descendentes diretas das classes do Qt, mas de classes criadas pela interface concebida com o auxílio do Designer.

Na a seção Criação das classes no KDevelop foi dito que, a partir da interface criada no Designer, são geradas classes que a implementam. Essas classes poderiam ser utilizadas para implementar o que foi modelado para o sistema, mas se alguma alteração for realizada na interface pelo Designer, ele recria os arquivos que implementam as classes e, portanto, a codificação teria que ser refeita. A melhor forma de trabalhar com classes de interface é criando-se classes filhas das criadas pelo Designer. Assim, mesmo que alguma modificação seja feita no Designer, a subclasse não é alterada e nenhum trabalho deve ser refeito. Este é o motivo pelo qual as classes KListaFormaPagto e KFormaPagto foram criadas como filhas de QListaFormaPagto e QFormaPagto criadas a partir da interface desenhada no Designer.

A classe KListaFormaPagto

A Figura 39 mostra a estrutura da classe KListaFormaPagto. Ela é declarada como filha de QListaFormaPagto que será gerada no momento da compilação do sistema. A sua função é montar uma lista de formas de pagamento cadastradas no banco de dados e fornecer meios para que o usuário possa incluir, alterar e excluir elementos desta lista. É importante notar que seus métodos, com exceção do construtor, são os mesmos métodos acrescentados no momento da criação da interface no Designer e representam os slots que vão tratar os eventos decorrentes da interação do usuário.

class KListaFormaPagto : public QListaFormaPagto
{
Q_OBJECT
public:
    KListaFormaPagto(QWidget *parent=0,
      const char *name=0, WFlags f=Qt::WDestructiveClose);

public slots:
    virtual void atualiza();
    virtual void inclui();
    virtual void exclui();
    virtual void altera();
};

Figura 39. Estrutura da classe KListaFormaPagto

Como pode ser visto na Figura 40 o construtor apenas chama o método atualiza() mostrado na mesma figura. Esse método é responsável pela montagem da lista de formas de pagamento cadatradas no sistema. Para obter os dados, ela usa uma instância de DListaFormapagto.

KListaFormaPagto::KListaFormaPagto(QWidget *parent,
  const char *name, WFlags f)
  : QListaFormaPagto(parent, name, f)
{
  atualiza();
}

void KListaFormaPagto::atualiza()
{

  listaFormaPagto->clear();

  DListaFormaPagto formaPagto;
  if (formaPagto.obtemDados())
    while (formaPagto.proximo())
      new KListViewItem(listaFormaPagto,
          formaPagto.obtemCampo("DESCRICAO"),
          formaPagto.obtemCampo("CODIGO"),
          formaPagto.obtemCampo("PRAZO"),
          formaPagto.obtemCampo("PESOCOM"),
          formaPagto.obtemCampo("PESODESC"));

}

Figura 40. Implementação do construtor e o método atualiza()

Os métodos inclui() e altera() mostrados na Figura 41 são muito parecidos. Ambos criam uma instância de DFormaPagto para manipular os dados da forma de pagamento diferindo apenas no construtor que é usado. A outra diferença entre os métodos é que em altera() é feito um teste para certificar que o usuário selecionou um elemento da lista para ser alterado.

void KListaFormaPagto::inclui()
{

  KFormaPagto *novaForma = new KFormaPagto(this,
    "novaFormaPagto", Qt::WDestructiveClose);
  if (novaForma->exec() == QDialog::Accepted)
    atualiza();
  delete novaForma;

}

void KListaFormaPagto::altera()
{

  if (!listaFormaPagto->selectedItem()){
    QMessageBox::warning(this, "Forma de pagamento",
      "Voc�deve selecionar uma forma de pagamento da "
      "lista antes de modificar.");
    return;
  }
  KFormaPagto *formaPagto = new KFormaPagto(
    listaFormaPagto->selectedItem()->text(1), this,
    "alteraFormaPagto", Qt::WDestructiveClose);
  if (formaPagto->exec() == QDialog::Accepted)
    atualiza();
  delete formaPagto;

}

Figura 41. Implementação dos métodos inclui() e altera()

O último método de KListaFormaPagto é o exclui(). Ele primeiramente verifica se foi selecionada uma forma de paamento da lista, depois avisa o usuário que a forma de pagamento será excluída do sistema e finalmente cria uma instânacia de DFormaPagto para excluir a forma de pagamento. A Figura 42 mostra a sua implementação.

void KListaFormaPagto::exclui()
{
  if (!listaFormaPagto->selectedItem()){
    QMessageBox::warning(this, "Forma de pagamento",
      "Voc�deve selecionar uma forma de pagamento da "
      "lista antes de apagar.");
    return;
  }

  if (QMessageBox::information(this, "Forma de pagamento",
      "A forma de pagamento abaixo: \n\n" +
      listaFormaPagto->selectedItem()->text(0)
      + "\n\nser�apagada do banco de dados.\n"
      "Voc�confirma a exclus�?",
      "&Sim", "&N�", 0, 0, 1) == 0)
  {
    DFormaPagto formaPagto(
      listaFormaPagto->selectedItem()->text(1));
    if (formaPagto.exclui()) atualiza();
  }

}

Figura 42. Implementação do método exclui()

A classe KFormaPagto

A classe KFormaPagto monta uma janela que permite ao usuário digitar os dados de uma forma de pagamento e gravá-la no sistema. Ela tem dois construtores, sendo um chamado quando uma forma de pagamento deve ser incluída e outro quando a forma de pagamento deve ser alterada. O atributo codigo irá armazenar o código da forma de pagamento que deve ser alterada. A Figura 43 mostra a sua estrutura.

class KFormaPagto : public QFormaPagto
{
public:
    KFormaPagto(QWidget *parent, const char *name=0,
      WFlags f=Qt::WDestructiveClose);
    KFormaPagto(QString fp, QWidget *parent=0,
      const char *name=0, WFlags f=Qt::WDestructiveClose);

    void grava(  );

private:
    QString codigo;

};

Figura 43. Estrutura da classe KFormaPagto

O primeiro construtor da classe, mostrado na Figura 44, deve ser chamado quando uma nova forma de pagamento estiver sendo adicionada ao sistema. Ele apenas cria validadores para impedir que o usuário digite dados inválidos. O segundo construtor, além de criar estes validadores, obtém a forma de pagamento cujo código foi passado como parâmetro e atualiza os campos da janela.

KFormaPagto::KFormaPagto(QWidget *parent,
  const char *name, WFlags f)
  : QFormaPagto(parent, name, f)
{
  codigo = "";

  QRegExp rx("^\\d?\\,\\d{2}$");
  linePesoCom->setValidator(
    new QRegExpValidator(rx, linePesoCom));
  linePesoDesc->setValidator(
    new QRegExpValidator(rx, linePesoDesc));
}

KFormaPagto::KFormaPagto(QString fp, QWidget *parent,
  const char *name, WFlags f)
  : QFormaPagto(parent, name, f)
{
  codigo = fp;

  QRegExp rx("^\\d?\\,\\d{2}$");
  linePesoCom->setValidator(
    new QRegExpValidator(rx, linePesoCom));
  linePesoDesc->setValidator(
    new QRegExpValidator(rx, linePesoDesc));

  // Obt� os dados da forma de pagamento cujo c�igo
  // �fp
  DFormaPagto formaPagto(codigo);
  lineDescricao->setText(
    formaPagto.obtemCampo("DESCRICAO"));
  linePesoCom->setText(
    formaPagto.obtemCampo("PESOCOM").replace(".", ","));
  linePesoDesc->setText(
    formaPagto.obtemCampo("PESODESC").replace(".", ","));
  comboPrazo->setCurrentItem(
    ((formaPagto.obtemCampo("PRAZO") == "S") ? 0 : 1) );

}

Figura 44. Construtores de KFormaPagto

O método grava() faz seu trabalho dependendo do atributo codigo. Se ele tiver um valor, a forma de pagamento deve ser alterada e então é criada uma instância de DFormaPagto chamando seu construtor que recebe o código da forma de pagamento a alterar. Caso o atributo esteja vazio, a instância de DFormaPagto é criada usando o outro construtor. Depois disso, os campos da forma de pagamento são alterados e enviados ao banco de dados. Finalmente se a gravação foi bem sucedida, o método accept() que encerra o processamento da janela e indica seu resultado como QDialog::accepted. O método é implementado de acordo com a Figura 45.

void KFormaPagto::grava()
{
  DFormaPagto *fp;
  if (codigo.isEmpty())
    fp = new DFormaPagto();
  else
    fp = new DFormaPagto(codigo);

  fp->alteraCampo(
    "DESCRICAO", lineDescricao->text());
  fp->alteraCampo(
    "PRAZO", comboPrazo->currentText().left(1));
  QString p = linePesoCom->text();
  p.replace(',', '.');
  fp->alteraCampo("PESOCOM", p);
  p = linePesoDesc->text();
  p.replace(',', '.');
  fp->alteraCampo("PESODESC", p);
  bool resultado = fp->grava();

  delete fp;
  if (resultado) accept(); else reject();

}

Figura 45. Implementação do método grava()

A função main()

Depois de implementadas as classes, deve-se alterar a função main() criada pelo KDevelop para chamar a classe KListaFormaPagto que vai dar acesso ao cadastro de formas de pagamento.

Uma parte do código gerado pelo KDevelop deverá ser ignorada, pois ele está chamando a classe Kvendas que ele criou junto do projeto. Para implementa¸ão do sistema completo esse código pode ser usado como modelo para chamar a classe que vai representar a janela principal do aplicativo.

#include "kvendas.h"
#include <kapplication.h>
#include <kaboutdata.h>
#include <kcmdlineargs.h>
#include <klocale.h>

#include "klistaformapagto.h"

static const char *description =
    I18N_NOOP("A KDE KPart Application");

static const char *version = "0.1";

static KCmdLineOptions options[] =
{
//{ "+[URL]", I18N_NOOP( "Document to open." ), 0 },
  { 0, 0, 0 }
};

int main(int argc, char **argv)
{
    KAboutData about("kvendas", I18N_NOOP("KVendas"),
                     version, description,
                     KAboutData::License_GPL,
                     "(C) 2004 Anderson Pereira Ataides",
                     0, 0, "anderson.pa@persogo.com.br");
    about.addAuthor("Anderson Pereira Ataides", 0,
                    "anderson.pa@persogo.com.br" );
    KCmdLineArgs::init(argc, argv, &about);
    KCmdLineArgs::addCmdLineOptions( options );
    KApplication app;
    KListaFormaPagto *mainWin = 0;

    KCmdLineArgs *args = KCmdLineArgs::parsedArgs();

    mainWin = new KListaFormaPagto(0, "listaFormaPagto");
    app.setMainWidget( mainWin );
    mainWin->show();
    args->clear();

    int ret = app.exec();

    delete mainWin;
    return ret;
}

As linhas 1 a 5 foram geradas pelo KDevelop e são necessárias devido a utilização de algumas estrutruras que estão declaradas nestes headers. O include da linha 7 já faz parte do projeto, pois é nesse arquivo que está declarada a classe KListaFormaPagto.

Da linha 9 à linha 34, são códigos gerados pelo KDevelop e são necessários porque o sistema é um sistema KDE e ele vai precisar dessa implementação. Dessas linhas, a mais importante é a 32, onde é criado o objeto app que representa a aplicação.

Na linha 36, é criado o objeto que é a janela de lista de formas de pagamento. Na linha 37 é informado ao aplicativo que o widget criado vai ser o principal. Na linha 38 o widget se torna visível. A linha 41 entra no loop da aplicação e o controle passa para a janela principal: o widget mainWin.

Quando a janela for fechada, a janela principal é encerrada e a aplicação sai do loop. Na linha 43, então o widget é desalocado e a execução do aplicativo é encerrada.

A implementação do caso de uso então está completa. Para ver o sistema funcionando é só mandar o KDevelop compilar e executar o sistema. Como pode-se ver na implementação, o conhecimento necessário de C++ se resume ao necessário para utilizar as bibliotecas escolhidas. Em nenhum momento foi necessário implementar rotinas de tratamento de tela, manipulação de arquivos ou conexões em baixo nível. Foram usadas apenas apenas funções pré-definidas nas bibliotecas.

A partir de agora a tarefa seria implementar cada um dos outros casos de uso e finalmente a janela principal do aplicativo para chamar cada uma das janelas que implementam os outros casos de uso. Para aprender mais sobre o KDevelop, e como programar aplicativos usando a biblioteca Qt, recomenda-se a leitura de [KDEVMANUAL] e [QTMANUAL].