«

»

Jan 19

Profiglacy – Nunca foi tão fácil escrever uma UI com Swing (Graças ao JRuby)

O nome é complicado mesmo, o nome da biblioteca é “Profiglacy“. O nome da biblioteca veio de um SPAM recebido pelo Zed Shaw e ele utiliza esta biblioteca para escrever um programa de nome iHate, que é um cliente para um protocolo parecido com IRC, mas com algumas coisas mais divertidas.

O JRuby é uma implementação da linguagem Ruby para rodar na JVM. Uma das vantagens de uma implementação de Ruby rodando em uma JVM é a possibilidade de tirar proveito de todas as outras coisas que também rodam na JVM.
Exemplos disto são o acesso a EJBs a partir de aplicações Ruby, utilização de código Legado Java, acesso a diversas bibliotecas que já existem para Java e ainda não existem para Ruby.
Outra grande vantagem é a possibilidade de escrever UIs utilizando SWING que é um dos frameworks para UI mais completos disponíveis hoje em dia, mas que quando utilizado com java, tem uma possibilidade muito grande de criar um código horrível.
E para solucionar este problema existe o Profiglacy, que é uma biblioteca Ruby para facilitar a utilização de SWING quando se esta trabalhando com o JRuby.

E neste pequeno tutorial vou criar uma aplicação simples utilizando esta biblioteca. A proposta é um Gerenciador de Tarefas bem simples, mas antes vamos ver do que estamos fugindo …

Criar UI em Java é muito flexível mas muito trabalhoso, o SWING é poderoso mas muito verboso, e a própria natureza do Ruby ja melhora um pouco isto, veja o código abaixo:

1
2
3
4
5
6
require 'java'
 
@frame = javax.swing.JFrame.new "Old Way, using only SWING from Ruby"
@frame.add(@lbl1 = javax.swing.JLabel.new("Master Title"))
@frame.pack
@frame.visible = true

Isto vai criar um jframe com um label, mas ainda assim iriamos precisar de todos aqueles gerenciadores de layout, além de ser necessário também atrelar a ordem de criação dos objetos a posição deles no layout, mas para tudo há uma solução.
O modo padrão de trabalho do Profiglacy melhora isto apenas um pouco, então vamos começar direto com a utilização da Layout Expression Language criada para utilização na biblioteca. É basicamente uma forma fácil de se utilizar um GridLayout …
O layout vai ser definido como uma String, o formato desta string é bastante simples:

  • [ .. ] – delimita o inicio e fim de uma linha
  • | – delimita uma celula da linha
  • label – qualquer nome utilizado dentro de uma celula se torna o nome da celula para referência posterior
  • _ – identifica uma celula em branco
  • (width) ou (width,height) – define a largura e/ou altura de um componente dentro da celula
  • * – Expande a celula
  • ^ ou . – Alinham o componente no topo ou na parte de baixo da celula respectivamente
  • < ou > – Alinham o componente a esquerda ou direita respectivamente

E é isto, simples assim …
Segue um exemplo para facilitar o entendimento:

1
2
3
4
5
[ <lbl_proj | cmb_project  ]
[ <lbl_activ | cmb_activ  ]
[ <lbl_date | inpt_date ]
[ <lbl_hour | inpt_hour ]
[ <lbl_description | (300,200)txt_description  ]

Este código define um Grid Layout de 5 linhas por duas colunas, todos os componentes da esquerda estão também alinhados a esquerda e o último componente da direita tem 300 pixels de largura por 200 de altura.

Agora algum de vocês se anima a escrever o código em java para montar isto? Não precisa nem usar o Grid Layout, garanto que vai ficar bem maior.

Claro que as vezes o LEL (Layout Expression Language) não é suficiente, mas para mim parece que isto torna 80% dos casos bastante simples, e quando for necessário isto sempre pode ser combinado com código padrão …

Deem uma olhada no código abaixo, é criada uma UI simples, combinando paineis.
O primeiro painel definido por “main_layout” organiza os grupos de componentes, o primeiro componente recebe um label e os outros dois recebem paineis, criados com LEL mas poderiam ser criados utilizando JPanel.new sem maiores problemas.
TaskManager.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
require 'java'
require 'rubygems'
require 'profligacy/swing'
require 'profligacy/lel'
 
module TaskManager
    class UI
      include_package 'javax.swing'
      include_package 'java.awt'
      include_package 'javax.swing.border'
      include Profligacy
 
      def initialize(title)
        main_layout = "[main_label][inputs][buttons]"
        layout = %Q{
        [ <lbl_proj | cmb_project  ]
        [ <lbl_activ | cmb_activ  ]
        [ <lbl_date | inpt_date ]
        [ <lbl_hour | inpt_hour ]
        [ <lbl_description | (300,200)txt_description  ]
        }
        @ui = Swing::LEL.new JFrame, main_layout do |c,i|
          c.main_label = JLabel.new "Information About  New Entry"
          c.inputs = Swing::LEL.new JPanel, layout do |d,i|
            d.lbl_proj = JLabel.new "Project"
            d.cmb_project = JTextField.new
            d.lbl_activ = JLabel.new "Activity"
            d.cmb_activ = JTextField.new
            d.lbl_date = JLabel.new "Date"
            d.inpt_date = JTextField.new
            d.lbl_hour = JLabel.new "Hour"
            d.inpt_hour = JTextField.new
            d.lbl_description = JLabel.new "Description"
            d.txt_description = JTextArea.new
          end.build :auto_create_container_gaps => false
          c.buttons = Swing::LEL.new JPanel, "[button_save|button_cancel ]" do |e,i|
            e.button_save = JButton.new "Save"
            i.button_save = { :action => method(:save_clicked) }
            e.button_cancel = JButton.new "Cancel"
          end.build :auto_create_container_gaps => false
        end
        @ui.build(:args => "Simple LEL Example").default_close_operation = JFrame::EXIT_ON_CLOSE
      end
 
      def save_clicked(evt_type,event)
        puts "Test OK 2"
      end
 
      def self.start
        SwingUtilities.invoke_later proc { UI.new('My Test Frame with long title') }.to_runnable
      end
 
    end
end

Para executar o código precisamos apenas de um arquivo Ruby para chamar o método “start” definido na classe UI, claro que poderiamos ter utilizado o mesmo arquivo .rb, mas isto iria diminuir a possibilidadede reutilização daquele código, então criei o arquivo abaixo:
starter.rb

1
2
3
require 'TaskManager'
 
TaskManager::UI.start

Pronto, com este super mini tutorial, você ja pode escrever muito menos código para definir as suas interfaces Java de hoje em diante, para isto só precisa programar em Ruby :D

Cada vez mais me convenço que o melhor cenário é utilizar java como Plataforma em vez de como Linguagem :D

PS.: Eu sei que faltou explicar toda a parte de eventos, mas se não for possível inferir isto do exemplo apresentado, postem perguntas nos comentários que escrevo mais algo detalhado sobre isto (isto vai servir também pra ver se alguem lê o que eu escrevo aqui :D )