Criando gráficos via AdvPL com FWChartBar

Olá pessoal…

Hoje vou mostrar um exemplo simples de como criar gráfico de barra em AdvPL, utilizando a classe FWChartBar.


Exemplo de FWCharBar

Exemplo de FWCharBar

Foram criadas novas classes para geração de gráficos substituindo a antiga TMSGraphic, abaixo uma lista das classes novas com o link para o TDN (todas herdam da FwChartFactory):
FWChartBar – Gráficos de Barra
FWChartBarComp – Gráficos de Barra (Comparação)
FWChartLine – Gráficos de Linha
FWChartPie – Gráficos de Pizza

E abaixo um exemplo simples de FWChartBar (do print acima):

//Bibliotecas
#Include "Protheus.ch"  

/*/{Protheus.doc} zTstChart
Função de teste da classe FWChartBar
@type function
@author Atilio
@since 01/12/2015
@version 1.0
	@example
	u_zTstChart()
	@see http://tdn.totvs.com/display/public/mp/FWChartBar
/*/

User Function zTstChart()
	Local oChart
	Local oDlg
	Local aRand := {}
	
	//Cria a Janela
	DEFINE MSDIALOG oDlg PIXEL FROM 0,0 TO 400,600
		//Instância a classe
		oChart := FWChartBar():New()
		
		//Inicializa pertencendo a janela
		oChart:Init(oDlg, .T., .T. )
		
		//Seta o título do gráfico
		oChart:SetTitle("Título", CONTROL_ALIGN_CENTER)
		
		//Adiciona as séries, com as descrições e valores
		oChart:addSerie("Ano 2011", 20044453.50)
		oChart:addSerie("Ano 2012", 21044453.35)
		oChart:addSerie("Ano 2013", 22044453.15)
		oChart:addSerie("Ano 2014", 23044453.10)
		oChart:addSerie("Ano 2015", 25544453.01)
		
		//Define que a legenda será mostrada na esquerda
		oChart:setLegend( CONTROL_ALIGN_LEFT )
		
		//Seta a máscara mostrada na régua
		oChart:cPicture := "@E 999,999,999,999,999.99"
		
		//Define as cores que serão utilizadas no gráfico
		aAdd(aRand, {"084,120,164", "007,013,017"})
		aAdd(aRand, {"171,225,108", "017,019,010"})
		aAdd(aRand, {"207,136,077", "020,020,006"})
		aAdd(aRand, {"166,085,082", "017,007,007"})
		aAdd(aRand, {"130,130,130", "008,008,008"})
		
		//Seta as cores utilizadas
		oChart:oFWChartColor:aRandom := aRand
		oChart:oFWChartColor:SetColor("Random")
		
		//Constrói o gráfico
		oChart:Build()
	ACTIVATE MSDIALOG oDlg CENTERED
Return

Atualização em 18/02/2017:
Consegui achar uma referência com algumas possíveis cores para utilização:

aAdd(aRand, {"199,199,070", "022,022,008"}) //Amarelo Escuro
aAdd(aRand, {"084,120,164", "007,013,017"}) //Azul Claro
aAdd(aRand, {"054,090,134", "007,013,017"}) //Azul Escuro
aAdd(aRand, {"175,175,175", "011,011,011"}) //Cinza Claro
aAdd(aRand, {"130,130,130", "008,008,008"}) //Cinza Médio
aAdd(aRand, {"100,100,100", "008,008,008"}) //Cinza Escuro
aAdd(aRand, {"207,136,077", "020,020,006"}) //Laranja Claro
aAdd(aRand, {"177,106,047", "020,020,006"}) //Laranja Escuro
aAdd(aRand, {"001,001,001", "001,001,001"}) //Preto
aAdd(aRand, {"141,225,078", "017,019,010"}) //Verde Claro
aAdd(aRand, {"171,225,108", "017,019,010"}) //Verde Escuro
aAdd(aRand, {"166,085,082", "017,007,007"}) //Vermelho Claro
aAdd(aRand, {"136,055,052", "017,007,007"}) //Vermelho Escuro

Atualização em 20/09/2017:
Conforme muitos pediam, montei um exemplo de como gerar o gráfico e já inserir ele em um relatório. Para fazer isso, é só ao iniciar a tela do relatório, gerar um png, e fechar a tela em seguida. Depois com essa imagem você consegue imprimir em um TMSPrinter ou FWMSPrinter.
Abaixo um print do resultado:

Exemplo de relatório FWMSPrinter com gráfico

Exemplo de relatório FWMSPrinter com gráfico

E abaixo o código fonte completo desenvolvido:

//Bibliotecas
#Include "Protheus.ch"
#Include "TopConn.ch"
#Include "RPTDef.ch"
#Include "FWPrintSetup.ch"

#Define PAD_LEFT	0
#Define PAD_RIGHT   1
#Define PAD_CENTER  2

/*/{Protheus.doc} zTeste
Função de teste para gerar o gráfico em um relatório
@author Atilio
@since 20/09/2017
@version undefined

@type function
/*/

User Function zTeste()
	Local aArea       := GetArea()
	Local cNomeRel    := "rel_teste_"+dToS(Date())+StrTran(Time(), ':', '-')
	Local cDiretorio  := GetTempPath()
	Local nLinCab     := 025
	Local nAltur      := 250
	Local nLargur     := 1050
	Local aRand       := {}
	Private cHoraEx    := Time()
	Private nPagAtu    := 1
	Private oPrintPvt
	//Fontes
	Private cNomeFont  := "Arial"
	Private oFontRod   := TFont():New(cNomeFont, , -06, , .F.)
	Private oFontTit   := TFont():New(cNomeFont, , -20, , .T.)
	Private oFontSubN  := TFont():New(cNomeFont, , -17, , .T.)
	//Linhas e colunas
	Private nLinAtu     := 0
	Private nLinFin     := 820
	Private nColIni     := 010
	Private nColFin     := 550
	Private nColMeio    := (nColFin-nColIni)/2
	
	//Criando o objeto de impressão
	oPrintPvt := FWMSPrinter():New(cNomeRel, IMP_PDF, .F., /*cStartPath*/, .T., , @oPrintPvt, , , , , .T.)
	oPrintPvt:cPathPDF := GetTempPath()
	oPrintPvt:SetResolution(72)
	oPrintPvt:SetPortrait()
	oPrintPvt:SetPaperSize(DMPAPER_A4)
	oPrintPvt:SetMargin(60, 60, 60, 60)
	oPrintPvt:StartPage()
	
	//Cabeçalho
	oPrintPvt:SayAlign(nLinCab, nColMeio-150, "Relatório Teste de Gráfico", oFontTit, 300, 20, RGB(0,0,255), PAD_CENTER, 0)
	nLinCab += 35
	nLinAtu := nLinCab
	
	//Se o arquivo existir, exclui ele
	If File(cDiretorio+"_grafico.png")
		FErase(cDiretorio+"_grafico.png")
	EndIf
	
	//Cria a Janela
	DEFINE MSDIALOG oDlgChar PIXEL FROM 0,0 TO nAltur,nLargur
		//Instância a classe
		oChart := FWChartBar():New()
		 
		//Inicializa pertencendo a janela
		oChart:Init(oDlgChar, .T., .T. )
		 
		//Seta o título do gráfico
		oChart:SetTitle("Título", CONTROL_ALIGN_CENTER)
		 
		//Adiciona as séries, com as descrições e valores
		oChart:addSerie("Ano 2011", 20044453.50)
		oChart:addSerie("Ano 2012", 21044453.35)
		oChart:addSerie("Ano 2013", 22044453.15)
		oChart:addSerie("Ano 2014", 23044453.10)
		oChart:addSerie("Ano 2015", 25544453.01)
		 
		//Define que a legenda será mostrada na esquerda
		oChart:setLegend( CONTROL_ALIGN_LEFT )
		 
		//Seta a máscara mostrada na régua
		oChart:cPicture := "@E 999,999,999,999,999.99"
		 
		//Define as cores que serão utilizadas no gráfico
		aAdd(aRand, {"084,120,164", "007,013,017"})
		aAdd(aRand, {"171,225,108", "017,019,010"})
		aAdd(aRand, {"207,136,077", "020,020,006"})
		aAdd(aRand, {"166,085,082", "017,007,007"})
		aAdd(aRand, {"130,130,130", "008,008,008"})
		 
		//Seta as cores utilizadas
		oChart:oFWChartColor:aRandom := aRand
		oChart:oFWChartColor:SetColor("Random")
		 
		//Constrói o gráfico
		oChart:Build()
	ACTIVATE MSDIALOG oDlgChar CENTERED ON INIT (oChart:SaveToPng(0, 0, nLargur, nAltur, cDiretorio+"_grafico.png"), oDlgChar:End())
	
	oPrintPvt:SayBitmap(nLinAtu, nColIni, cDiretorio+"_grafico.png", nLargur/2, nAltur/1.6)
	nLinAtu += nAltur/1.6 + 3
	
	oPrintPvt:SayAlign(nLinAtu, nColIni+020, "Teste FWMSPrinter",                            oFontSubN, 100, 07, , PAD_LEFT, )
	
	//Impressão do Rodapé
	fImpRod()
	
	//Gera o pdf para visualização
	oPrintPvt:Preview()
	
	RestArea(aArea)
Return

/*---------------------------------------------------------------------*
 | Func:  fImpRod                                                      |
 | Desc:  Função para impressão do rodapé                              |
 *---------------------------------------------------------------------*/

Static Function fImpRod()
	Local nLinRod := nLinFin + 10
	Local cTexto  := ""

	//Linha Separatória
	oPrintPvt:Line(nLinRod, nColIni, nLinRod, nColFin, RGB(200, 200, 200))
	nLinRod += 3
	
	//Dados da Esquerda
	cTexto := "Relatório Teste    |    "+dToC(dDataBase)+"     "+cHoraEx+"     "+FunName()+"     "+cUserName
	oPrintPvt:SayAlign(nLinRod, nColIni,    cTexto, oFontRod, 250, 07, , PAD_LEFT, )
	
	//Direita
	cTexto := "Página "+cValToChar(nPagAtu)
	oPrintPvt:SayAlign(nLinRod, nColFin-40, cTexto, oFontRod, 040, 07, , PAD_RIGHT, )
	
	//Finalizando a página e somando mais um
	oPrintPvt:EndPage()
	nPagAtu++
Return

Bom pessoal, por hoje é só.
Abraços e até a próxima.

Dan Atilio (Daniel Atilio)
Especialista em Engenharia de Software pela FIB. Entusiasta de soluções Open Source. E blogueiro nas horas vagas.

28 Responses

  1. Mário Sérgio disse:

    No meu caso funcionou tudo, menos a questão de cores. Ele não está deixando escolher as cores que quero, independente se coloco random ou se seto uma cor fixa. Ele só fica cor padrão.

    • Dan_Atilio disse:

      Boa noite Mário, tudo bem?
      Realizei um teste aqui, realmente são só algumas cores que ele aceita, irei verificar se encontro alguma referência.
      Abraços.

      • Mário Sérgio disse:

        Inclusive to fazendo o gráfico do tipo pizza e também não consigo alterar as cores… Liguei no suporte da Totvs e eles me falaram que não tem como alterar as cores… Que vai pegar sempre as cores padrões…

        • Dan_Atilio disse:

          Boa noite Mario, tudo bem?
          Então, cacei aqui nas minhas referências, e ele realmente deixa a cor padrão (azulada), mas é possível definir algumas cores (poucas), no caso desse exemplo mesmo eu uso laranja, vermelho, cinza, etc).
          Infelizmente não achei nenhuma documentação sobre as cores aceitas.

  2. Mário Sérgio disse:

    Obrigado!
    Se um dia descobrir algo sobre mudança de cores, eu me interesso.

  3. Thiago disse:

    Consegue-se desenhar o FWChartBar num panel, restrigindo as dimensões do próprio FWChartBar ?

    • Dan_Atilio disse:

      Fala Thiago, tudo bem?
      É possível sim, geralmente é o que eu faço, onde meu Painel (oPanelTst), vai pertencer a minha Dialog (oDialogTst) e o gráfico estará no Painel (oChartTst), por exemplo:

      oPanelTst := tPanel():New(nLinha, nColuna, "", oDialog, , , , RGB(000,000,000), RGB(254,254,254), nLargGraf, nAltuGraf)
      oChartTst := FWChartBar():New()
      oChartTst:Init(oPanelTst, .T., .T.)
      

      Espero ter ajudado.
      Um grande abraço.

  4. Joao Carlos disse:

    Eu não to conseguindo fazer mostrar os valores das séries, existe alguma forma de mostrar o valor? Aqui só mostra se eu posicionar o cursor, queria que aparecesse junto a série.

    • Dan_Atilio disse:

      Boa noite João, tudo bem?
      Eu também não consegui achar uma forma, então como paliativo, eu coloco como título da série, por exemplo:

      nValor := 5000
      oChartTst:addSerie("Série X "+Alltrim(Transform(nValor, "@E 99,999.99")), nValor)
      

      Espero ter ajudado.
      Abraços.

  5. Boa tarde amigo!

    Há a possibilidade de definir a escala do gráfico? Tentei utilizar SetMinY e SetMinX, porém os dois métodos parecem não funcionar.

    • Dan_Atilio disse:

      Boa noite Murilo, tudo bem?
      Rapaz, não sei te dizer, eu nunca precisei mudar a escala.
      E quando eu fiz, lembro de ter dividido os valores para ficar menor o gráfico.
      Acho que cabe um chamado nessa questão.
      Um grande abraço.

  6. jose fernando disse:

    como posso imprimir o grafico???

    • Dan_Atilio disse:

      Boa noite Jose, tudo bem?
      Você teria que ao abrir a tela com o gráfico, gerar um png dele, e esse png você imprime em um TMSPrinter ou FWMSPrinter.
      Eu atualizei a postagem com um exemplo completo, espero que ajude.
      Abraços.

  7. Pergunta cabulosa #1, consigo gerar estes graficos num job, e enviar por email, via scheduler ? ? ?

    Obrigado. MAterial muito bom !

    • Dan_Atilio disse:

      Boa noite.
      Sim, você deve usar o método SaveToPng, http://tdn.totvs.com/pages/releaseview.action?pageId=111182092
      Depois disso, você consegue manipular a imagem conforme você deseja.
      Para gerar o png, ao iniciar a janela, você deve dar um End, e já gerar em seguida, por exemplo:

      DEFINE MSDIALOG oDlgChar PIXEL FROM 0,0 TO nAltur,nLargur
      ....
      nDescLarg := 30
      ACTIVATE MSDIALOG oDlgChar CENTERED ON INIT (oChart:SaveToPng(nDescLarg, 0, nLargur-nDescLarg, nAltur, cDiretorio+"grafico.png"), oDlgChar:End())
      

      Depois do arquivo gerado, ai você pode mandar por email, colocar em relatórios, etc…
      Qualquer dúvida, fico à disposição.
      Abraços.

  8. eduardsjo disse:

    Boa tarde.
    Há algum método para limpar as series do grafico.
    Estou utilizando um grid que toda vez que clico preenche um grafico com os dados.
    Quando clico pela segundavez no grid ele acumula os valores preenchidos anteriormente e adiciona os novos.
    Obriago,

    Eduardo

    • Dan_Atilio disse:

      Boa noite Eduardo, tudo bem?
      Rapaz, desconheço se existe algum método assim, talvez será necessário abrir um chamado e perguntar, mas talvez pode ser difícil que respondam algo tão técnico.
      Um grande abraço man.

  9. eduardsjo disse:

    Boa Tarde Dan Atilio.

    Consegui o efeito desejado, instanciando novamente o objeto toda vez antes de inserir os itens da série.

    o comando é: oChartTst := FWChartBar():New()

    O suporte da Totvs ainda não respondeu se está é a solução definitiva…u

  10. eduardsjo disse:

    Falaram para utilizar desta forma mesmo. Acho que nunca tiveram esta necessidade ou não esta previsto este tipo de uso.
    Abraços,

  11. polti disse:

    Boa tarde, teria algum jeito de, ao colocar um conteúdo muito grande como por exemplo 50 descrições ele gerar uma nova página com a continuação dos dados, tipo 25 pra cada página?

  12. Carlos Eduardo Júnior disse:

    Estou desenvolvendo uma customização com esse recurso, mas somente tenho conseguido salvar o arquivo local no client, como faço para salvar no servidor?

    • Dan_Atilio disse:

      Boa noite Carlos, tudo bem? Desconheço como salvar direto no Servidor, o que você pode fazer, é gerar localmente, e usar CpyT2S para copiar para dentro da Protheus Data. Um grande abraço.

  13. Vagner José dos Santos disse:

    Boa tarde Dan!

    Tenho o fonte abaixo onde é realizado um cadastro simples e tenho seu fonte em uma static function grafic onde é criado o grafico!
    A pergunta é como que coloco esse gráfico na parte inferior do browser?

    #Include ‘Protheus.ch’
    #Include ‘FWMVCDef.ch’
    #INCLUDE “JURA039.CH”

    //Variáveis Estáticas
    Static cTitulo := “teste”

    //——————————————————————-
    /*/{Protheus.doc} INFJ0020

    @since 27/01/2021
    @version 1.0
    /*/
    //——————————————————————-

    User Function TST0020()
    Local aArea := GetArea()
    Local oBrowse

    //Instânciando FWMBrowse – Somente com dicionário de dados
    oBrowse := FWMBrowse():New()

    //Setando a tabela de cadastro de Autor/Interprete
    oBrowse:SetAlias(“ZZF”)

    //Setando a descrição da rotina
    oBrowse:SetDescription(cTitulo)

    //Legendas
    oBrowse:AddLegend( “ZZF->ZZF_ATIVO == ‘2’”, “GREEN”, “Ativo” )
    oBrowse:AddLegend( “ZZF->ZZF_ATIVO == ‘1’”, “RED”, “Encerrado” )

    //Ativa a Browse
    oBrowse:Activate()

    RestArea(aArea)
    Return Nil

    //——————————————————————-
    /*/{Protheus.doc} MenuDef
    Menu Funcional

    @return aRotina – Estrutura
    [n,1] Nome a aparecer no cabecalho
    [n,2] Nome da Rotina associada
    [n,3] Reservado
    [n,4] Tipo de Transação a ser efetuada:
    1 – Pesquisa e Posiciona em um Banco de Dados
    2 – Simplesmente Mostra os Campos
    3 – Inclui registros no Bancos de Dados
    4 – Altera o registro corrente
    5 – Remove o registro corrente do Banco de Dados
    6 – Alteração sem inclusão de registros
    7 – Cópia
    8 – Imprimir
    [n,5] Nivel de acesso
    [n,6] Habilita Menu Funcional

    @since 27/01/2021
    @version 1.0
    /*/
    //——————————————————————-

    Static Function MenuDef()
    Local aRotina := {}

    aAdd( aRotina, { STR0001, “PesqBrw” , 0, 1, 0, .T. } ) // “Pesquisar”
    aAdd( aRotina, { STR0002, “VIEWDEF.INFJ0020”, 0, 2, 0, NIL } ) // “Visualizar”
    aAdd( aRotina, { STR0003, “VIEWDEF.INFJ0020”, 0, 3, 0, NIL } ) // “Incluir”
    aAdd( aRotina, { STR0004, “VIEWDEF.INFJ0020”, 0, 4, 0, NIL } ) // “Alterar”
    aAdd( aRotina, { STR0005, “VIEWDEF.INFJ0020”, 0, 5, 0, NIL } ) // “Excluir”
    aAdd( aRotina, { STR0006, “VIEWDEF.INFJ0020”, 0, 8, 0, NIL } ) // “Imprimir”

    Return aRotina

    //——————————————————————-
    /*/{Protheus.doc} ModelDef
    Modelo de dados do Business Plan

    @since 27/01/2021
    @version 1.0
    /*/
    //——————————————————————-

    Static Function ModelDef()
    Local oModel := Nil
    Local oStructZZF := FWFormStruct(1, ‘ZZF’)
    Local usuario := UsrRetName(RetCodUsr())

    //Criando o modelo e os relacionamentos
    oModel := MPFormModel():New(‘INFJ20’)
    oModel:AddFields(‘ZZFMASTER’,/*cOwner*/,oStructZZF)

    oModel:SetPrimaryKey({})

    //Setando as descrições
    oModel:SetDescription(“teste”)
    oModel:GetModel(‘ZZFMASTER’):SetDescription(‘teste’)
    Return oModel

    //——————————————————————-
    /*/{Protheus.doc} ViewDef

    @since 27/01/2021
    @version 1.0
    /*/
    //——————————————————————-

    Static Function ViewDef()
    Local oView := Nil
    Local oModel := FWLoadModel(‘TST0020’)
    Local oStructZZF := FWFormStruct(2, ‘ZZF’)

    //Criando a View
    oView := FWFormView():New()
    oView:SetModel(oModel)

    //Adicionando os campos do cabeçalho e o grid dos filhos
    oView:AddField(‘VIEW_ZZF’,oStructZZF,’ZZFMASTER’)

    //Setando o dimensionamento de tamanho
    oView:CreateHorizontalBox(‘CABEC’,100)

    //Amarrando a view com as box
    oView:SetOwnerView(‘VIEW_ZZF’,’CABEC’)

    Return oView

    //——————————————————————-
    /*/{Protheus.doc} ViewDef

    @since 27/01/2021
    @version 1.0
    /*/
    //——————————————————————-

    user Function UALT0020()
    Local cUsuario := UsrRetName(RetCodUsr())

    if M->ZZF_ATIVO == ‘1’
    ZZF->ZZF_DTENC := DATE()
    ZZF->ZZF_USRENC := usuario
    endif

    Return cUsuario

    //——————————————————————-
    /*/{Protheus.doc} ViewDef

    @since 27/01/2021
    @version 1.0
    /*/
    //——————————————————————-

    User Function GRAFIC()
    Local oChart
    Local oDlg
    Local aRand := {}

    //Instância a classe
    oChart := FWChartBar():New()

    //Inicializa pertencendo a janela
    oChart:Init(oDlg, .T., .T. )

    //Seta o título do gráfico
    oChart:SetTitle(“Título”, CONTROL_ALIGN_CENTER)

    //Adiciona as séries, com as descrições e valores
    oChart:addSerie(“Ano 2011”, 20044453.50)
    oChart:addSerie(“Ano 2011”, 15044453.50)
    oChart:addSerie(“Ano 2012”, 21044453.35)
    oChart:addSerie(“Ano 2012”, 12044453.35)
    oChart:addSerie(“Ano 2013”, 22044453.15)
    oChart:addSerie(“Ano 2013”, 18044453.15)
    oChart:addSerie(“Ano 2014”, 23044453.10)
    oChart:addSerie(“Ano 2014”, 9044453.10)
    oChart:addSerie(“Ano 2015”, 25544453.01)
    oChart:addSerie(“Ano 2015”, 20544453.01)

    //Define que a legenda será mostrada na esquerda
    oChart:setLegend( CONTROL_ALIGN_LEFT )

    //Seta a máscara mostrada na régua
    oChart:cPicture := “@E 999,999,999,999,999.99”

    //Define as cores que serão utilizadas no gráfico
    aAdd(aRand, {“084,120,164”, “007,013,017”})
    aAdd(aRand, {“171,225,108”, “017,019,010”})
    aAdd(aRand, {“084,120,164”, “007,013,017”})
    aAdd(aRand, {“171,225,108”, “017,019,010”})
    aAdd(aRand, {“084,120,164”, “007,013,017”})
    aAdd(aRand, {“171,225,108”, “017,019,010”})
    aAdd(aRand, {“084,120,164”, “007,013,017”})
    aAdd(aRand, {“171,225,108”, “017,019,010”})

    //Seta as cores utilizadas
    oChart:oFWChartColor:aRandom := aRand
    oChart:oFWChartColor:SetColor(“Random”)

    //Constrói o gráfico
    oChart:Build()

    Return

    Consegue me ajudar?
    Abraços!

    • Dan_Atilio disse:

      Bom dia Vagner, tudo bem?
      Eu nunca precisei adicionar um gráfico em um FWmBrowse, mas comecei a procurar por alguns exemplos, não encontrei muita coisa, apenas a menção à um método chamado SetChartsDefault, tente talvez utilizá-lo.
      Se conseguir algo me avise também.
      Grande abraço.

Deixe uma resposta