TableModel e Threads - problemas com o Swing

Acho q a maioria aqui sabe do esquema single thread do Swing, então vejam o meu problema.
Estou utilizando threads para fazer com que o swing não pare de desenhar a tela. Entretanto, meu TableModel recebe um resultado de uma query que é um resultado sob demanda, ou seja, o getValue efetivamente busca o resultado em um buffer no application server. O problema é que quando esse resultado não está no buffer é feita mais uma parte da consulta (a consulta é em lotes) e essa consulta pode ser lenta. Isso faz com que, por exemplo, se eu sair da tela e voltar ele não conseguirá repintar a tela e ficará aquele bloco cinza. Fiz alguns testes, mas nenhum foi realmente eficaz.
Alguém já passou por um problema parecido???

Vallew

Pelo que entendi, vc criou uma thread pra repintar a tela separado de tudo, neh ?
Acho que o buraco nao eh na hora de redesenhar, e sim na hora de buscar o valor ( getValue() )…
Ja tentou fazer o getValue() em outra thread() ??? Talvez ai ele consiga repintar enquanto espera o resultado…

E ai brlima!!! Valeu a resposta!!

Mas veja bem, o getValueAt só é chamado quando necessário…
Por exemplo, se vc criar uma JTable e jogar, por exemplo, 1000 linhas para o seu model. Colocar essa JTable em um JScrollPane e aparecerem apenas 10 linhas, o getValueAt do model será chamado apenas 10 vezes.
E assim funciona o esquema de carga sobre demanda, quando o getValueAt é chamado pelo JTable é feita a carga, e somente ai!

Dessa maneira o JTable só chamará o getValueAt realmente quando estiver sendo pintado…

A única coisa que me ocorreu foi tentar fazer um load prévio de um certo número de dados, mas mesmo assim não é uma solução ideal.

Por enquanto to aqui me quebrando.

Hmmm… Disso eu sabia…r.s… :roll:
Mas achei que vc queria buscar direto no servidor toda vez que ele desse um getValueAt().

Pq nao faz assim: separa o cara que busca no servidor. Toda vez que ele der um getValueAt() verifica o numero da linha que ele ta chamando, e se for mais que o numero de linhas que vc trouxe, busca mais dados. Acho que deve ser o jeito. Guardar de pouco em pouco num “buffer” interno do modelo. Dai, a cada 50* linhas ele busca mais no Servidor… Ou ateh mesmo bota um timer de 10 segundos… dai a cada 10 segundos vc busca de novo os dados…

Eu implementei algo parecido numa JTable: qdo ele chega na ultima linha, verifica se ainda tem mais coisa pra trazer do banco: ele busca de 50 em 50… e conforme vc vai baixando o scroll ( indo pra linha debaixo ) ele vai trazendo mais se necessario…

Será ue estamos sintonizados no mesmo canal? rssssssss…

Essa é a idéia, ele só carrega o que precisa, ou seja, ele só carrega quando se rola o scroll. Porém, esse é o problema.
Seguinte, vou te dar uma idéia da estrutura que tenho aqui:
quando faço uma query, por exemplo de um produto, o cliente requisita para o JBoss (através de SessionBean) executar a query, o mesmo devolve um objeto List, bem esse list na verdade possui um buffer interno de n registros (definidos na hora da criação), essa list vai no TableModel. Assim, quando se está pintando a tela ele requisita pro model que pega da list, o list por sua vez verifica se esse index está no seu buffer ou não. Se não estiver ele possui uma referência (já criada no momento que a query é chamada pela primeira vez) para um Session Stateful, esse Stateful tbm possui um buffer, dessa maneira eu possuo um buffer a nível de cliente e um a nível de application server. Com essa referencia, o list do cliente pede blocos de n registros(tbm definidos na criação), até conseguir acessar o indice pediod pelo cliente.
Só que eu não sei quantos registros serão apresentados na tela, pois o JTable vai pedindo diretamente para o TableModel, e quem controla a quantidade é o JTable.

Acho q estamos começando a nos entender :lol: .
É uma situação bem complexa, será que eu tenho como saber quantos registros o JTable precisa, acho que não pois um model pode estar sendo usado por 2 JTable e cada um pedir uma quantidade diferente de registros!!!

Sei lá, vou continuar pensando, se te ocorrer algo me avisa!!!

Vallew

AQUem controla a quantidade é o TableModel: nao se esqueça do método getRowCount() :smiley:
Esse método retorna o numero de linhas que o modelo tem, para a jtable criar suas linhas.

Bem, uma ideia que posso te dar seria de vc botar um listener no scroll, para lançar um evento qdo atingir a ultima linha “visivel”. Ou seja, qdo ele rolar o scroll ateh embaixo. Dai, nesse evento, va verificar se essa ultima linha eh realmente a ultima linha do buffer, e se nao tem mais nada pra carregar. Se tiver, vai la no server, carrega tudo, volta pro client, carrega MAIS no modelo, e lança que o modelo foi alterado.

Nesse ultimo caso, nao precisa nem mexer no getRowCount. Deixa como esta, pq vai acontece assim: Situação:

Cliente requisita todos os pedidos dele numa JTable:
(FIND): Resultado total: 200 ( mas vamos carregar de 50 em 50 )
carrega o list com os 50 primeiros. retorna o list
carrega o TableModel com o list.

Ok Cliente vendo resultado. Agora ele vai rolando o scroll pra baixo pra ver o pedido 54. Qdo ele atingi a linha 50:
carregaMaisDadosNoModel();{
list = vai no servidor, pega da linha 50 pra frente.
list tem alguma coisa? tem, mais 50…
retorna list dos 50 novos!

modelo = modelo + list.
dispara modelo alterado!
}

pronto. o scroll vai pra linha 51 e carrrega ateh o 100. o cliente pode continuar o scoroll na boa… E assim vai ateh o proximo bloco.

Será que isso funciona ai ??? :roll:

O q acontece hoje realmente é isso q tu falou o list é responsável por busca mais linhas no server.

Pelo que sei o JTable calcula o scroll baseado no getRowCount, ou não é isso???

Dessa maneira, se tiver 200 registros eu precisos devolver 200 no getRowCount pois se devolver apenas 50 a rolagem não aparecerá ou aparecerá menor do q realmente é.

Mas tudo bem, eu não vou mexer no getRowCount então.

Agora no seu exemplo veja o problema, aparece os 50 e o cliente rola para aparecer até o 54, então se nesse momento eu buscar do server já acontecerá o problema (na verdade é mais ou menos assim que funciona hj), o tempo que estiver buscando no server o swing não poderá pintar a tela.

Foi isso??? Ou agora eu q não pesquei a sua idéia??? :slight_smile:

Vallew

Pegou sim.
Realmente, o tempo de busca ( demorado ) fica travado… O que vc pode fazer seria meter uma dialog informado que ta obtendo mais…
Fica meio complicado do cara continuar descendo e nao ter o que mostrar, nao concorda ?

Com relação ao scroll, ele mostra mesmo soh o que o TableModel tem, e nao o total…

Vc pode colocar um botao do lado dizendo “Mais dados…” :smiley: pra carregar mais na JTable… hehehehehhe

Nao sei se convem deixar a tela bonitinha sem nada… acho que o usuario vai achar que num tem mais nada pra trazer…

Ou faz que nem o Kazaa: vai buscando de 10 em 10 toda horas :smiley: em uma thread separada… hehehehehee… Ai o scroll vai aumentando ateh buscar tudo~…

Flw!

Olá

Threads - problemas com o Swing? Seus problemas acabaram! Use Foxtrot, an easy and powerful API to use threads with the JavaTM Foundation Classes (JFC/Swing).

[]s
Luca

Pois é Luca, eu estava fazendo alguns testes do foxtrot, mas não cheguei a usá-lo para testar com esse problema…
Vou fazer um teste e aviso se funcionar, valeu!!!

Olá

Ted, pode estar certo que com foxtrot acabam as telas cinza enquanto algo é feito em outtra thread. Se tiver tempo estude a teoria dele, se não tiver tempo de entende-lo aprenda a usa-lo que é bem rápido.

[]s
Luca

Não sei se eu estou fazendo algo errado, mas o Foxtrot não me ajudou no problema. Na verdade ele consegue pintar a tela, mas então a pintura se perde completamente, pintando partes fora do JInternalFrame.
O que eu fiz foi mais ou menos o seguinte:

  • o getValueAt() do meu TableModel ficou mais ou menos assim
public Object getValueAt(int rowIndex, int columnIndex) {
  if (data != null) {
    final Object obj = data.get(rowIndex);
    Object retorno = Worker.post(new Job() {
      public Object run() {
        //processamento pesado sobre o Obj
        return obj;
      }
    });
    return retorno;
  }
  return null;
}

Pelo que vi no tips & tricks é necessário cuidar nos modelos swing com o acesso de duas threads ao mesmo, mas não conseguik achar o problema!!!

Ahhh, antes que eu me esqueça, durante o paint ele retornou esse erro:

Foxtrot - Exception occurred during event dispatching:
java.lang.NullPointerException
	at javax.swing.SwingUtilities.computeIntersection(SwingUtilities.java:369)
	at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:404)
	at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(SystemEventQueueUtilities.java:117)
	at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:178)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:454)
	at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:201)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:151)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:145)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:137)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:100)

Vou continuar tentando aqui, se alguém (Luca???) já passou por esse problema e tiver alguma dica, valeu!

Olá

Rodrigo, não tenho aqui no escritório exemplos de uso do foxtrot. De noite em casa posso procurar. Mas parece que seu código está diferente do modelo deles. Confira com: http://foxtrot.sourceforge.net/docs/foxtrot.php

[]s
Luca

Eu dei uma lida nos baratos da homepage la ( le-se tutoriais ) e pelo que entendi, ele trabalha com uma Thread separada pra acessar os dados, :shock: é isso ?

Testei aqui alguns exemplos usando Threads mesmo, tratando tudo bonitinho, funfa bacana… Mas foi um teste basico: abre processo para buscar no banco, soh… durante a busca, deixei tudo congelado ( enabled false ) na tela… ou oderia colocar um jdialog… :roll:

Alguem sabe exeplicar a real vantagem de se trabalhar com esse foxtrote ai ? ao inves de ter Threads ?? :roll: :?: :?: :wink:

Pelo q entendi, a vantagem é que vc trabalha de uma maneira sincrona, ou seja, no caso de usar threads a linha seguinte a chamada para thread pode ser executada antes, depois ou durante a execução da thread. O foxtrot te garante que o código da task ou job será executada, e apenas após a execução o código seguinte será executado.
Veja o ex.:

btn.addActionListener(new ActionListener() {
  public void actionPerformed(ActionEvent e) {
    Worker.post(new Job() {
      public Object run() {
        try {
          Thread.sleep(5000);
        } catch (InterruptedException e) {}
        System.out.println("primeiro Eu");
        return null;
      }
    });
    System.out.println("cheguei");
  }
});

Dessa maneira o ‘primeiro eu’ é impresso e depois é impresso o ‘cheguei’…
Se vc usasse uma thread assim:

btn.addActionListener(new ActionListener() {
  public void actionPerformed(ActionEvent e) {
    Thread t = new Thread(new Runnable() {
      public void run() {
         try {
          Thread.sleep(5000);
         } catch (InterruptedException e) {}
         System.out.println("primeiro Eu");
      }
    });
    t.start();
    System.out.println("cheguei");
  }
});

Aqui o cheguei será impresso antes.

No link que o Luca mandou tem uma lista de vantagens que a abordagem te tráz.

Entretanto, as vezes vc precisa de uma abordagem assincrona, ai vc usa threads…

Fallow

Olá

A principal vantagem é não deixar aquela tela cinza horrível (The GUI freeze problem) enquanto executa alguma função por baixo dos panos. A idéia começou com a classe SwingWorker. Veja:[list]How to Use Threads

SwingWorker Update Fixes Subtle Bug

Customize SwingWorker to improve Swing GUIs [/list]A API foxtrot dá uma modificada nos conceitos de SwingWorker. É preciso ler a documentação e comparar com os problemas de SwingWorker para perceber as vantagens.

[]s
Luca

Nao por isso Ted…
Trabalhando com Threads, vc pode dar um yield() no processo principal, deixando o processo em espera executar primeiro logo depois de dar um start() … Fiz um teste aqui e ele fez igual o exemplo que vc me mostrou.

Tentei usar join(), e nesse caso ele esperou a paralela terminar para voltar a principal ( processo normal da coisa… )

Acho q na verdade vc teria que ter processos de paint separado dos processos de ação na aplicação… Assim ele pintaria tudo idependemente de estar aguardando um processo disparado pelo evento de clicar no botao.

Como ficou o seu código com o Yeld???

Vo posta aqui, pq testei numa aplicação minha… :smiley: Que faz consulta no banco e carrega uma list…


          Runnable processo = new Runnable(){
              public void run(){
                  
                  System.out.println("Rodei...");
                   /* Processo do banco de dados demorado... */

              }
          };
          


         Thread t = new Thread(processo);
          t.start();

          /*try{
            t.join();
          }catch(InterruptedException ie){
              ie.printStackTrace();
          }*/

          Thread.yield();
          System.out.println("Iniciei");

Comentei o join(), mas se vc descomenta e comentar o yield, vera que ele espera o processo anterior terminar… o yield, como diz nos livros, libera o processador para um processo rodar. Entra na fila de novo pra rodar. :smiley:

Hmmm, estranho!!!
Eu fiz o teste com o yeld, porém ele imprimiu o cheguei primeiro… :?