First usage of TBROWSE

278 views
Skip to first unread message

Mario Emmanuel

unread,
Oct 10, 2023, 7:33:32 AM10/10/23
to Harbour Users
Hi,

I am trying to get an initial basic TBROWSE example working.

This is the two procedures used for:
1) create de DBF
2) display the DBF contents as a TBROWSE object.

The DBF has been previously created successfully and database has been opened in the MAIN procedure.

However while calling this one I get:

Error BASE/1004 Message not found: TBROWSE:SHOW

Can someone let me know what would be wrong/missing?

Thanks

---

#include "hbclass.ch"


PROCEDURE CreateCustomerDatabase


// DEFINE FIELDS

LOCAL aStruct := {}

AAdd( aStruct, { "C_ID", "C", 10, 0 } )

AAdd( aStruct, { "C_NANE", "C", 80, 0 } )

AAdd( aStruct, { "C_COMNAME", "C", 80, 0 } )

AAdd( aStruct, { "C_ADDR1", "C", 80, 0 } )

AAdd( aStruct, { "C_ADDR2", "C", 80, 0 } )

AAdd( aStruct, { "C_ADDR3", "C", 80, 0 } )

AAdd( aStruct, { "C_VAT", "C", 20, 0 } )

AAdd( aStruct, { "C_COUNTRY", "C", 80, 0 } )

AAdd( aStruct, { "C_NOTE1", "C", 80, 0 } )

AAdd( aStruct, { "C_NOTE2", "C", 80, 0 } )

AAdd( aStruct, { "C_NOTE3", "C", 80, 0 } )

AAdd( aStruct, { "C_NOTE4", "C", 80, 0 } )

AAdd( aStruct, { "C_NOTE5", "C", 80, 0 } )


// CREATE THE DBF FILE

IF ! dbCreate( "data/Customers.dbf", aStruct )

? "Error: Could not create Customers.dbf"

RETURN

ENDIF


// RETURN NORMALLY

RETURN


PROCEDURE DisplayCustomerList


// CREATE TBROWSE OBJECT AND COLUMNS

LOCAL oBrowse

LOCAL oColumn1, oColumn2, oColumn3, oColumn4, oColumn5, oColumn6, oColumn7, oColumn8

oBrowse := TBrowseDB ( 5, 10, 20, 70 )


oColumn1 := TBColumnNew("ID", {||Customer->C_ID} )

oColumn2 := TBColumnNew("Name", {||Customer->C_NAME} )

oColumn3 := TBColumnNew("Commercial Name", {||Customer->C_COMNAME} ) 

oColumn4 := TBColumnNew("Address1", {||Customer->C_ADDR1} )

oColumn5 := TBColumnNew("Address2", {||Customer->C_ADDR2} )

oColumn6 := TBColumnNew("Address3", {||Customer->C_ADDR3} )

oColumn7 := TBColumnNew("VAT", {||Customer->C_VAT} )

oColumn8 := TBColumnNew("Country", {||Customer->C_COUNTRY} )


    // Display and event loop

    DO WHILE .T.

        oBrowse:Show()

        nKey := Inkey(0)

        nEvent := oBrowse:EvaluateKey( nKey )

        IF nEvent == xbeK_ESC

            EXIT

        ENDIF

    ENDDO


    RETURN

CV

unread,
Oct 10, 2023, 7:48:10 AM10/10/23
to Harbour Users
Hi Mario

There is no :SHOW() method in tbrowse.

Please use something like this:

---
#include "TBrowse.ch"
PROCEDURE Main
LOCAL oTBrowse, aFields, cField, nKey
 
USE Customers new shared
aFields := array(FCount())
 
AEval( aFields, {|x,i| aFields[i] := FieldName(i)})  
 
oTBrowse := TBrowseDB()
WITH OBJECT oTBrowse
    FOR EACH cField IN aFields
         :addColumn( TBColumnNew( cField, FieldBlock( cField )))  // or use your own way to configure the columns
    NEXT 
END

WHILE .T. 
     oTBrowse:forceStable()  
     nKey := Inkey(11)  // will update the browse display if something changes in the source database after 11 seconds
     IF oTBrowse:applyKey(nKey) == TBR_EXIT  // with the standard navigation keys 
         EXIT 
     ENDIF 
ENDDO 
CLOSE Customers
RETURN 
---

Adapt it to your needs.

Regards
--
Claudio Voskian 
Buenos Aires - Argentina

Mario Emmanuel

unread,
Oct 10, 2023, 11:57:17 AM10/10/23
to Harbour Users
Hi Claudio

I have tried both your direct code and a slight variation from your suggested code which shall be easier to read as a first example:

---

PROCEDURE DisplayCustomerList

        // CREATE TBROWSE OBJECT AND COLUMNS
        LOCAL oBrowse, aColObjects[4]
        LOCAL nKey, nCnt

        oBrowse := TBrowseDB ( 5, 10, 20, 70 )
        aColObjects[1] := TBColumnNew("ID", {|| Customer->C_ID} )
        aColObjects[2] := TBColumnNew("Name", {|| Customer->C_NAME} )
        aColObjects[3] := TBColumnNew("Commercial Name", {|| Customer->C_COMNAME} )
        aColObjects[4] := TBColumnNew("Address 1", {|| Customer->C_ADDR1} )
   
        FOR nCnt := 1 TO LEN(aColObjects)
                oBrowse:addColumn(aColObjects[nCnt])
        NEXT

        WHILE .T.
                oBrowse:forceStable()
                nKey := Inkey(0)
                IF nKey == K_ESC
                        EXIT
                ENDIF
        END
---

I believe the code above should work. However, I only see one field (ID). The database is empty yet properly structured, as confirmed by the output generated by the following code:

  1 PROCEDURE DisplayDatabase
  2
  3         LOCAL nTotalFields, n
  4
  5         USE data/Customer.dbf
  6
  7
  8         nTotalFields := FCOUNT()
  9         FOR n := 1 TO nTotalFields
 10                 ? "Field " + STR(n) + ": " + FIELDNAME(n) + ", Type: " + FIELDTYPE(n) + ",     Length: " + STR(FIELDLEN(n))
 11         NEXT
 12
 13         USE
 14        
 15 RETURN

Field          1: C_ID, Type: C, Length:         10                                                          
Field          2: C_NAME, Type: C, Length:         80                                                        
Field          3: C_COMNAME, Type: C, Length:         80                                                      
Field          4: C_ADDR1, Type: C, Length:         80                                                        
Field          5: C_ADDR2, Type: C, Length:         80                                                        
Field          6: C_ADDR3, Type: C, Length:         80                                                        
Field          7: C_VAT, Type: C, Length:         20                                                          
Field          8: C_COUNTRY, Type: C, Length:         80                                                      
Field          9: C_NOTE1, Type: C, Length:         80                                                        
Field         10: C_NOTE2, Type: C, Length:         80                                                        
Field         11: C_NOTE3, Type: C, Length:         80                                                        
Field         12: C_NOTE4, Type: C, Length:         80                                                        
Field         13: C_NOTE5, Type: C, Length:         80

I am currently stuck on this issue, and any assistance would be greatly appreciated. Do you have any recommended reading material for handling TBROWSE? I understand that TBROWSE is a fundamental component in Harbour.

Thanks,

Auge & Ohr

unread,
Oct 10, 2023, 6:16:21 PM10/10/23
to Harbour Users
hi,

you NEED a "Skipper" to show more than 1 Record

FUNCTION DbfSkipper( nWantSkip )
LOCAL nDidSkip := 0

      DO CASE
      CASE LastRec() == 0              

      CASE nWantSkip == 0
         GOTO RecNo()                

      CASE nWantSkip > 0    
         DO WHILE nWantSkip > nDidSkip .AND. .NOT. Eof()
            SKIP
            IF Eof()                  
               SKIP -1
               EXIT
            ENDIF
            nDidSkip ++
         ENDDO

      CASE nWantSkip < 0            
         DO WHILE nWantSkip < nDidSkip .AND. .NOT. Bof()
            SKIP -1
            IF Bof()
               EXIT
            ENDIF
            nDidSkip --
         ENDDO
      ENDCASE
RETURN nDidSkip

and "fill" TBROWSE Codeblock Slot

      oTBrowse := TBrowse():new( nTop, nLeft, nBottom, nRight )
     // NEED to "fill" for Navigation
      oTBrowse:goTopBlock    := {|| DbGoTop() }
      oTBrowse:goBottomBlock := {|| DbGoBottom() }
      oTBrowse:skipBlock     := {|n| DbfSkipper(n) }
      // Option
      oTBrowse:headSep       := Chr(205) + Chr(205) + Chr(205)
      oTBrowse:colSep        := Chr(32)  + Chr(179) + Chr(32)
      oTBrowse:colorSpec     := "N/BG,W+/B,N/R,W+/R"

and a Function for "navigation"

FUNCTION TBNavigate( oTBrowse, nKey )
LOCAL bBlock  := SetKey( nKey )
LOCAL lReturn := .T.

      DO CASE
      CASE bBlock <> NIL        ; Eval( bBlock, oTBrowse )
      CASE nKey == K_DOWN       ; oTBrowse:down()
      CASE nKey == K_PGDN       ; oTBrowse:pageDown()
      CASE nKey == K_CTRL_PGDN  ; oTBrowse:goBottom()
      CASE nKey == K_LEFT       ; oTBrowse:left()
      CASE nKey == K_CTRL_LEFT  ; oTBrowse:panLeft()
      CASE nKey == K_HOME       ; oTBrowse:home()
      CASE nKey == K_CTRL_HOME  ; oTBrowse:panHome()
      CASE nKey == K_RIGHT      ; oTBrowse:right()
      CASE nKey == K_CTRL_RIGHT ; oTBrowse:panRight()
      CASE nKey == K_END        ; oTBrowse:end()
      CASE nKey == K_CTRL_END   ; oTBrowse:panEnd()
      CASE nKey == K_UP         ; oTBrowse:up()
      CASE nKey == K_PGUP       ; oTBrowse:pageUp()
      CASE nKey == K_CTRL_PGUP  ; oTBrowse:goTop()
      OTHERWISE
         lReturn:= .F.
      ENDCASE

RETURN lReturn

last is your Main-Loop which should like this

      DO WHILE nKey <> K_ESC
         DO WHILE .NOT. oTBrowse:stabilize()
            IF (nKey := Inkey(0.1)) <> 0
               EXIT
            ENDIF
         ENDDO

         IF oTBrowse:stable
            nKey := Inkey(0.1)
         ENDIF

         TBNavigate( oTBrowse, nKey )
      ENDDO

Note : i´m not sure how harbour will use INKEY() in a "Console" App
under Xbase++ i need INKEY(0.1) while i have use INKEY(0) under Cl*pper

Jim

CV

unread,
Oct 11, 2023, 2:02:46 PM10/11/23
to Harbour Users
Hi Mario

Do you use the cursor keys (arrows) to move on your CUSTOMERS database? I mean, the right arrow in fact -->>
As you have fields 80 character wide, maybe they not fit in your console screen if you didn't scroll to the rigth.

Use: setmode(40, 120) at the beggining of your main() function and probably you will see at least the 2nd field.

Other method: after opening the database, use

CUSTOMERS->(BROWSE())
Source code is in the distro, in \harbour\src\rtl

Regards
---
Claudio Voskian
Buenos Aires - Argentina

Mario Emmanuel

unread,
Oct 11, 2023, 3:13:12 PM10/11/23
to Harbour Users
Hi Claudio,

You have been sharpenly identified the issue. I have expanded the TBrowse object to 110 columns and now I see first two fields.
While I am currently reading about the TBrowse object in a book, I will still share three questions directly related to this thread:

1. Why the arrow keys do not work in my case?
2. How can you limit the width of the columns? So in the database the NAME might be 80 columns width, but in this TBrowse object it might be for example that only the first 20 are displayed.
3. If I want just to list the rows (records) of the database, but not actually allow edit or individual field navigation, is TBrowse the most canonical way of implementing this in Clipper/Harbour?

This is the source code I am currently using:

PROCEDURE DisplayCustomerList


        // CREATE TBROWSE OBJECT AND COLUMNS
        LOCAL oBrowse, aColObjects[4]
        LOCAL nKey, nCnt
        USE data/Customer
        oBrowse := TBrowseDB ( 2, 2, 40, 110 )

        aColObjects[1] := TBColumnNew("ID", {|| Customer->C_ID} )
        aColObjects[2] := TBColumnNew("Name", {|| Customer->C_NAME} )
        aColObjects[3] := TBColumnNew("Commercial Name", {|| Customer->C_COMNAME} )
        aColObjects[4] := TBColumnNew("Address 1", {|| Customer->C_ADDR1} )
       
        FOR nCnt := 1 TO LEN(aColObjects)
                oBrowse:addColumn(aColObjects[nCnt])
        NEXT

        //oColumn1 := TBColumnNew("C_ID", {||Customer->C_ID} )
        //oColumn2 := TBColumnNew("C_NAME", {||Customer->C_NAME} )
        //oColumn3 := TBColumnNew("C_COMNAME", {||Customer->C_COMNAME} )
        //oColumn4 := TBColumnNew("C_ADDR1", {||Customer->C_ADDR1} )
        //oColumn5 := TBColumnNew("C_ADDR2", {||Customer->C_ADDR2} )
        //oColumn6 := TBColumnNew("C_ADDR3", {||Customer->C_ADDR3} )
        //oColumn7 := TBColumnNew("C_VAT", {||Customer->C_VAT} )
        //oColumn8 := TBColumnNew("C_COUNTRY", {||Customer->C_COUNTRY} )

        WHILE .T.
                oBrowse:forceStable()
                nKey := Inkey(0)
                IF nKey == K_ESC
                        EXIT
                ENDIF
        END

RETURN

Thanks in advance,

CV

unread,
Oct 13, 2023, 10:58:17 AM10/13/23
to Harbour Users
Hi Mario

I can't figure out why the arrow keys doesn't work fine for you, but test it with a properly filled database.

You can configure the contents of any column, formatting them with a function in the codeblock, as in:
oColumn2:= TBColumnNew("C_NAME", {|| lower (left ( Customer->C_NAME, 25 ) ) } )
So you can use any valid expression inside the codeblock.

Finally, you can inspect the browse.prg I mentioned before to have a good idea about how to implement your own browser function.

Regards
--
Claudio Voskian
Buenos Aires - Argentina

Reply all
Reply to author
Forward
0 new messages