Página 10 de 10

cor no tbrowse

Enviado: 10 Set 2021 01:57
por alxsts
Olá!
JoséQuintas escreveu:O que o Alexandre mostrou NÃO daria pra usar.
Porque? porque na montagem da lista NÃO EXISTE oBrowse.
Exatamente por isto que corrigi o teu fonte postado em 03 Set 2021 12:45. Neste teu fonte, você inclui o oBrowse na chamada da função tBrowseADO2 e nos parâmetros do code block da rotina de usuário, sem que o oBrowse tivesse sido declarado e criado. Isto gerou erro e eu corrigi chamando a criação do browse antes, fora da rotina TBrowseADO2(). A prova que deu pra usar é o print da tela...

No meu fonte em 09 Set 2021 02:35:

Código: Selecionar todos

110	   oBrowse := CriaBrowse( 2, 2, MaxRow() - 5, MaxCol() - 2, oRs, aCamposList )
111	 
112	   bUserFunction := { |oBrowse, aCamposList, nKey| RotinaUsuario( oBrowse, aCamposList, nKey ) }
113	 
114	   TBrowseADO2( oBrowse, oRs, aCamposList, bUserFunction )
O teu fonte em 03 Set 2021 12:45: não tem a declaração do oBrowse mas referencia ele na linha 50.

Código: Selecionar todos

18	FUNCTION Tbrowseado(cSql)
19	   LOCAL oRs, aCamposList
20	   LOCAL oCN := ConexaoMySQL( "mysql.xxx.com.br", "xxx", "xxx", "xxx" )
21	   local tela :=savescreen(0,0,maxrow(),maxcol())
22	 
23	   Set( _SET_CODEPAGE, "PTISO" )
24	   Set( _SET_EVENTMASK, INKEY_ALL + HB_INKEY_GTEVENT - INKEY_MOVE )
25	   SET DATE BRITISH
26	 
27	*   hb_gtInfo( HB_GTI_WINTITLE , "TBrowse colorBlock() com Acesso ao MySQL via ADO" )
28	*   hb_gtInfo( HB_GTI_FONTNAME , "Lucida Console" )
29	   SetColor( "W/B","N/W",,,"W/B" )
30	   CLS
31	 
32	   oCn:open()
33	 
34	   DO WHILE .T.
35	      @ 4, MaxRow() SAY Padr( "Obtendo informações...", 22 ) COLOR "W/W"
36	      oRs := oCn:Execute( cSql )
37	      SuperADO( oRs )
38	      IF oRs == NIL .OR. oRs:Eof()
39	         Hb_Alert( "Não foi possível obter dados para exibição",, "W+/B" )
40	         EXIT
41	      ENDIF
42	      aCamposList := { ;
43	         { "DATA",       { || oRs:ToDate( "DATA" ) } }, ;
44	         { "HORA",       { || oRs:ToString( "HORA", 8 ) } }, ;
45	         { "COMIDA",     { || oRs:ToString( "COMIDA", 30 ) }, { | x | If( At( "TOTAL ", Upper( x ) ) > 0, { 5, 5 }, { 1, 2 } ) } }, ;
46	         { "QUANTIDADE", { || oRs:ToString( "QUANTIDADE", 20 )  } }, ;
47	         { "PONTOS",     { || oRs:ToStr( "PONTOS", 6 ) } }, ;
48	         { "ID",         { || oRs:ToStr( "ID", 6 ) } } }
49	 
50	      tBrowseADO2( oRs, aCamposList, { | oBrowse, nKey | RotinaUsuario( oRs, oBrowse, nkey ) } )
51	*      IF Alert( "Fechar?", { " Não ", " Sim " }, "W+/N" ) == 2

cor no tbrowse

Enviado: 10 Set 2021 07:35
por JoséQuintas
alxsts escreveu:O teu fonte em 03 Set 2021 12:45: não tem a declaração do oBrowse mas referencia ele na linha 50
ERRADO. Tem a declaração do oBrowse sim.

Código: Selecionar todos

50         tBrowseADO2( oRs, aCamposList, { | oBrowse, nKey | RotinaUsuario( oRs, oBrowse, nkey ) } )
Tá no codeblock.
Vai ser repassado pela rotina de browse.

Código: Selecionar todos

| oBrowse, nKey |
RotinaUsuario( oRs, oBrowse, nKey )
Na rotina BrowseADO() tem isto:

Código: Selecionar todos

Eval( bFuncaoUsuario, oBrowse, nKey )
oBrowse sai de BrowseADO() para a função de usuário, através do codeblock. A função de chamada nem sabe que isso existe, apenas cria o codeblock repassando.

cor no tbrowse

Enviado: 10 Set 2021 10:39
por JoséQuintas
Recapitulando

É TUDO EXEMPLO, não é pra usar exatamente igual.

Uma rotina principal do aplicativo

Código: Selecionar todos

MEMVAR cnSQL

FUNCTION Main()

   PUBLIC cnSQL := ConexaoMySQL()

   Set( _SET_CODEPAGE, "PTISO" )
   Set( _SET_EVENTMASK, INKEY_ALL + HB_INKEY_GTEVENT - INKEY_MOVE )
   SET DATE BRITISH
   SetMode(40,100)
   SetColor( "W/B","N/W",,,"W/B" )
   CLS
   cnSQL:Open()
   RotinaBrowse()
   cnSQL:Close()

   RETURN Nil
O módulo do aplicativo que vai usar o browse genérico.

Código: Selecionar todos

FUNCTION RotinaBrowse()

   LOCAL oRs, aCamposList

   DO WHILE .T.
      oRs := ADOExecute( cnSQL, "SELECT * FROM Any" )
      aCamposList := { ;
         { "DATA",       { || oRs:ToDate( "DATA" ) } }, ;
         { "HORA",       { || oRs:ToString( "HORA", 8 ) } }, ;
         { "COMIDA",     { || oRs:ToString( "COMIDA", 30 ) }, { | x | If( At( "TOTAL ", Upper( x ) ) > 0, { 5, 5 }, { 1, 2 } ) } }, ;
         { "QUANTIDADE", { || oRs:ToString( "QUANTIDADE", 20 )  } }, ;
         { "PONTOS",     { || oRs:ToStr( "PONTOS", 6 ) } }, ;
         { "ID",         { || oRs:ToStr( "ID", 6 ) } } }

      BrowseADO( oRs, aCamposList, { | oBrowse, nKey | RotinaUsuario( oRs, oBrowse, nkey ) } )
*      IF Alert( "Fechar?", { " Não ", " Sim " }, "W+/N" ) == 2
         oRs:Close()
         EXIT
*      ENDIF
   ENDDO

RETURN Nil

STATIC FUNCTION RotinaUsuario( oRs, oBrowse, nKey )

   IF nKey == K_F5
      Alert( "Foi teclado F5" )
      Alert( oRs:ToString( "COMIDA" ) )
      oBrowse:RefreshAll()
   ENDIF

RETURN 1
A rotina de browse genérica.

Código: Selecionar todos

#pragma -w3
#pragma -es2

#include "tbrowse.ch"
#include "inkey.ch"
#include "box.ch"

FUNCTION BrowseADO( oRs, aCamposList, bExecutaRotinaUsuario )

   LOCAL oBrowse, nKey, oTela

   IF oRs == Nil
      hb_Alert( "Sem informações para exibição" )
      RETURN Nil
   ENDIF

   SAVE SCREEN TO oTela

   oBrowse := CriaBrowse( oRs, aCamposList )

   DispBox( oBrowse:nTop - 1, oBrowse:nLeft - 1, oBrowse:nBottom + 3, oBrowse:nRight + 1, B_SINGLE )

   DO WHILE .T.

      oBrowse:refreshCurrent()
      DO WHILE ! oBrowse:Stable()
         oBrowse:Stabilize()
      ENDDO
      // Paint TBrowse current line...
      //oBrowse:ColorRect( { oBrowse:RowPos, oBrowse:LeftVisible, oBrowse:RowPos, oBrowse:RightVisible }, { 2, 1 } )

      @ MaxRow() - 1, 3 SAY Padr( " Registro " + Ltrim( Str( oRs:AbsolutePosition ) ) + " de " + Ltrim( Str( oRs:recordCount ) ) + " ", 20 ) COLOR "N/W"

      nKey := Inkey(0)
      IF oBrowse:applyKey( nKey ) == TBR_EXIT
         EXIT
      ENDIF
      IF bExecutaRotinaUsuario != NIL
         DO WHILE ! oBrowse:Stable
            oBrowse:Stabilize()
         ENDDO
         Eval( bExecutaRotinaUsuario, oBrowse, nKey )
         oBrowse:RefreshAll()
      ENDIF

   ENDDO

   RESTORE SCREEN FROM oTela

   RETURN Nil

STATIC FUNCTION CriaBrowse( oRs, aCamposList )
   LOCAL oBrowse, oColumn, aItem, nLen, nCont

   oBrowse := TBrowse():new( 02, 3, MaxRow() - 3, MaxCol() - 3 )
   oBrowse:headSep       := Chr(196) + Chr(194) + Chr(196)
   oBrowse:colSep        := " " + Chr(179) + " "
   oBrowse:footSep       := Chr(196) + Chr(193) + Chr(196)
   oBrowse:goTopBlock    := { || oRs:moveFirst() }
   oBrowse:goBottomBlock := { || oRs:moveLast() }
   oBrowse:skipBlock     := { |n| ADORecordSetSkipper( oRs,n ) }
   oBrowse:colorSpec     := "W/B,W+/N,N/W*,W+/R,R+/B,R/W*"

   IF aCamposList == Nil
      nLen := oRs:fields():count() - 1
      FOR nCont := 0 TO nLen
         oColumn := TBColumnNew( oRs:fields( nCont ):name(), ADORecordSetFieldBlock( oRs, nCont ) )
         oColumn:width := Max( Min( oRs:Fields( nCont ):definedSize,50), Len( oRs:fields( nCont ):name ) ) + 5
         oBrowse:addColumn( oColumn )
      NEXT
   ELSE
      FOR EACH aItem IN aCamposList
         oColumn := TBColumnNew( aItem[1], aItem[2] )
         IF Len( aItem ) > 2
            oColumn:ColorBlock := aItem[3]
         ENDIF
         oBrowse:AddColumn( oColumn )
      NEXT
   ENDIF
RETURN oBrowse

STATIC FUNCTION ADORecordSetFieldBlock( oRs, i, xVal )
   LOCAL bRet

   IF xVal == NIL
      IF oRs:Eof()
         bRet := { || Space( Max( oRs:Fields( i ):DefinedSize , Len( oRs:Fields( i ):name ) ) ) }
      ELSE
         bRet := { || oRs:Fields( i ):value }
      ENDIF
   ELSE
      bRet := { |xVal| oRs:Fields( i ):Value := xVal }
   ENDIF

RETURN bRet

STATIC FUNCTION ADORecordSetSkipper(oRecordSet,nSkip)
   LOCAL nRec := oRecordSet:AbsolutePosition

   IF ! ( oRecordSet:eof )
      oRecordSet:Move( nSkip )

      IF oRecordSet:eof
         oRecordSet:moveLast()
      ENDIF

      IF oRecordSet:bof
         oRecordSet:moveFirst()
      ENDIF
   ENDIF

RETURN (oRecordSet:AbsolutePosition - nRec)
Rotinas pra facilitar o ADO

Código: Selecionar todos


FUNCTION SuperADO( oRs )

   __ObjAddMethod( oRs, "TOSTRING", @ADOToString() )
   __ObjAddMethod( oRs, "TONUMBER", @ADOToNumber() )
   __ObjAddMethod( oRs, "TODATE",   @ADOToDate() )
   __ObjAddMethod( oRs, "TOSTR",    @ADOToStr() )

RETURN Nil

STATIC FUNCTION ADOToDate( cField )
   LOCAL x, Self := QSelf()

   x := ::Fields( cField ):Value
   IF ValType( x ) != "D"
      x := Ctod("")
   ENDIF
RETURN x

STATIC FUNCTION ADOToString( cField, nLen )
   LOCAL x, Self := QSelf()

   x := ::Fields( cField ):Value
   IF ValType( x ) != "C"
      x := ""
   ENDIF
   IF nLen != Nil
      x := Pad( x, nLen )
   ENDIF
RETURN x

STATIC FUNCTION ADOToNumber( cField )
   LOCAL x, Self := QSelf()

   x := ::Fields( cField ):Value
   IF ValType( x ) != "N"
      x := 0
   ENDIF

RETURN x

STATIC FUNCTION ADOToStr( cField, nLen, nDec )
   LOCAL x, Self := QSelf()

   x := ::Fields( cField ):Value
   IF ValType( x ) != "N"
      x := 0
   ENDIF
   IF nLen == Nil
      x := Str( x )
   ELSEIF nDec == Nil
      x := Str( x, nLen )
   ELSE
      x := Str( x, nLen, nDec )
   ENDIF

RETURN x

FUNCTION ConexaoMySQL()

   LOCAL cServer   := "mysql.xxx.com.br"
   LOCAL cDatabase := "xxx"
   LOCAL cUser     := "xxx"
   LOCAL cPassword := "xxx"

   LOCAL cnSQL := win_OleCreateObject("ADODB.Connection")

   cnSQL:ConnectionString := "DRIVER={MariaDB ODBC 3.0 Driver};TCPIP=1;SERVER=" + ;
      cServer + ";Database=" + cDatabase + ";UID=" + cUser + ";PWD=" + cPassword + ";PORT=3306"
   cnSQL:CursorLocation   := 3

RETURN cnSQL

FUNCTION ADOExecute( cnSQL, cSQL )

   LOCAL oRs

   oRs := cnSQL:Execute( cSQL )
   SuperADO( oRs )

   RETURN oRs

Cada uma trata de "seu assunto".
Temos uma genérica pra browse, e uma genérica pra ADO.
As outras, são exemplos de rotinas comuns do aplicativo.

Se não quiser usar as rotinas pra facilitar o ADO, ou quiser usar outras, é só trocar.

Se quiser fazer o principal de outro jeito, é só fazer

Se quiser fazer o módulo de outro jeito, é só fazer.

Se quiser alterar o browse genérico para, por exemplo, poder escolher uma determinada posição na tela, é só alterar.

Mas separando "por assunto", é mexer só na parte que interessa mexer.
Deixando tudo junto, fica parecendo que é tudo uma coisa só, e acaba programando tudo dependente um do outro, ou só servindo pra uma rotina.

cor no tbrowse

Enviado: 11 Set 2021 01:53
por cjp
Recapitulando

É TUDO EXEMPLO, não é pra usar exatamente igual.
Esta parte está toda certa aqui. Já entendi exatamente como funciona e já está funcionando, inclusive já estou criando outras funções específicas usando esta parte genérica.

Mas ainda assim está dando erro nos parâmetros: acesso de array.
Esta é a única questão que ainda não consegui resolver.

cor no tbrowse

Enviado: 11 Set 2021 09:11
por JoséQuintas
Erro no array:

Acessar elemento que não existe.
Verifique a linha do erro pra tentar determinar.

início do array geral : { ;
Cada elemento do array geral é o array de coluna { }, ;
Última coluna com finalização { } }

Cada coluna é um array com 2 ou 3 sub-elementos: um texto, um codeblock, e o codeblock de cor não obrigatório.

Código: Selecionar todos

{ "texto", { || campo } }
ou
{ "texto", { || campo }, { || cor } } }
Se por acaso colocar a vírgula em lugar errado, pode considerar que só tem um elemento ao invés de 2 ou 3.
Pode estar criando a cor como sendo parte do array geral, e não do array de colunas, caso tenha uma chave extra após a definição de campo.

exemplo com chave a mais, que faz com a cor seja uma nova coluna, ao invés de fazer parte da coluna em questão

Código: Selecionar todos

{ "texto", { || campo } }, { || cor } }
Isso faz com que a cor NÃO seja o terceiro elemento, e cria um novo incompleto, de 1 só ao invés de 2 ou 3.
A chave a mais depois de CAMPO faz com que encerre a coluna e inicie uma nova.

Eu falei que essa parte confunde, por usar muita separação. {}

Tente alinhar, se possível, os elementos, assim chama a atenção aonde estiver errado.

cor no tbrowse

Enviado: 11 Set 2021 16:37
por alxsts
Olá!
JoséQuintas escreveu:Tá no codeblock.
Vai ser repassado pela rotina de browse.
Tem razão. Na verdade, o código que consertei é do Inácio, cheio de bugs. Mas pensei que esta parte ele tivesse copiado do teu código.
JoséQuintas escreveu:essa parte confunde, por usar muita separação. {}
Tente alinhar, se possível, os elementos, assim chama a atenção aonde estiver errado.
Talvez assim seja mais fácil para ler:

Código: Selecionar todos

      aCamposList := {}

      AAdd( aCamposList, { "DATA",       { || oRs:ToDate( "DATA" ) } } )
      AAdd( aCamposList, { "HORA",       { || oRs:ToString( "HORA", 8 ) } } )
      AAdd( aCamposList, { "COMIDA", ;
                           { || oRs:ToString( "COMIDA", 30 ) }, ;
                           { |x| If( At( "TOTAL ", Upper( x ) ) > 0, { 5, 5 }, { 1, 2 } ) } ;
                         } )
      AAdd( aCamposList, { "QUANTIDADE", { || oRs:ToString( "QUANTIDADE", 20 )  } } )
      AAdd( aCamposList, { "PONTOS",     { || oRs:ToStr( "PONTOS", 6 ) } } )
      AAdd( aCamposList, { "ID",         { || oRs:ToStr( "ID", 6 ) } } )

cor no tbrowse

Enviado: 11 Set 2021 20:37
por JoséQuintas
Ou... se o bloco de cores é o que está atrapalhando... e se vai destacar a linha inteira... adiciona o bloco de cores a todos os elementos depois.

Código: Selecionar todos

LOCAL aCampo, bColorBlock
LOCAL aCamposList := ;
   { ;
      { "DATA",       { || oRs:ToDate( "DATA" ) } }, ;
      { "HORA",       { || oRs:ToString( "HORA", 8 ) } }, ;
      { "COMIDA",     { || oRs:ToString( "COMIDA", 30 ) }, ;
      { "QUANTIDADE", { || oRs:ToString( "QUANTIDADE", 20 ) } }, ;
      { "PONTOS",     { || oRs:ToStr( "PONTOS", 6 ) } }, ;
      { "ID",         { || oRs:ToStr( "ID", 6 ) } } ; // ultimo nao tem virgula no final
   )

bColorBlock := { || iif( At( "TOTAL ", Upper( oRs:ToString( "COMIDA" ) ) ) > 0, { 5, 5 }, { 1, 2 } ) }
FOR EACH aCampo IN aCampoList
   AAdd( aCampo, bColorBlock )
NEXT

cor no tbrowse

Enviado: 11 Set 2021 23:07
por cjp
Realmente tinha vírgula sobrando. Corrigi e parou de dar erro.

Mas, estranhamente, ainda não está funcionando a cor no campo PONTOS.

Por favor, verifique se ainda estou fazendo algo errado:

Código: Selecionar todos

      aCamposList := { ;
         { "DATA",       { || oRs:ToDate( "DATA" ) } }, ;
         { "HORA",       { || oRs:ToString( "HORA", 8 ) } }, ;
         { "COMIDA",     { || oRs:ToString( "COMIDA", 30 ) }, { | x | If( At( "TOTAL ", Upper( x ) ) > 0, { 5, 5 }, { 1, 2 } ) } }, ;
         { "QUANTIDADE", { || oRs:ToString( "QUANTIDADE", 20 )  } }, ;
         { "PONTOS",     ;
		   { || oRs:ToStr( "PONTOS", 9, 1 ) }, ;
		   { ||  If( At( "TOTAL ", oRs:ToString( "COMIDA" ) ) > 0, { 5, 5 }, { 1, 2 } ) } ;
		   } ;
		   }

cor no tbrowse

Enviado: 12 Set 2021 04:48
por JoséQuintas
Compare o que funciona com o que não funciona.
No que funciona, está usando UPPER()

cor no tbrowse

Enviado: 13 Set 2021 00:09
por cjp
Realmente, tinha esquecido do Upper. Obrigado. Funcionou.

cor no tbrowse

Enviado: 05 Out 2021 00:55
por cjp
Por favor, como faço para atualizar o browse após teclar alguma coisa na função de usuário?
Explico melhor: queria que, quando o usuário teclasse determinadas teclas, depois de feita a alteração correspondente, o browse voltasse atualizado; sem fechar o browse.
Exemplo: digamos que o usuário tecle Alt-A, para alterar o conteúdo de algum registro. Depois de feita a alteração, queria já exibir no browse essa alteração.
Atualmente, não está exibindo. Mas está alterando corretamente. Se eu fechar e reabrir o browse, volta atualizado. Mas eu queria que ele já voltasse atualizado sem precisar fechar e reabrir, entende?

cor no tbrowse

Enviado: 05 Out 2021 14:03
por alxsts
Olá!

Creio que seria melhor abrir um novo tópico e postar o código mostrando como está fazendo.

O tópico atual, além de estar saturado, não tem relação com a dúvida postada no post anterior.