BaCon Bits

For discussions about programming, programming questions/advice, and projects that don't really have anything to do with Puppy.
Message
Author
User avatar
sunburnt
Posts: 5090
Joined: Wed 08 Jun 2005, 23:11
Location: Arizona, U.S.A.

#31 Post by sunburnt »

Thanks again GatorDog; I don`t know why I didn`t find it the first time.
Looking at the docs it looks like it`s much more capable than gtkDialog.
It even has the old Basic drawing commands: circle, square, and pixel.
Can you say... Custom controls? :wink:

I signed up at the BaCon forum, see you there!

User avatar
GatorDog
Posts: 138
Joined: Tue 12 Sep 2006, 16:43

Hello Bacon (World)

#32 Post by GatorDog »

A project to demo some of the BaCon HUG widgets.
This program demos the COMBO widget ie. drop down box.

This is the naming convention that I've started using.
It should prevent collisions with items used in the compiler.
  • ALL_UPPER_CASE ( All uppercase for SUB's and FUNCTIONs )
    Unnnn_nnnn_ ( Uppercase first letter, end with "_" for widget handles )
    Unnnn_nnnn ( Variable names )
    Unnnn_nnnn$ ( String variables )
NEW -
The GLOBAL statement.
Although not strictly necessary, declaring variables becomes
more useful the bigger a program gets. The "TYPE NUMBER" declares
these variables to be numbers. We can have as many GLOBAL statements
as we want.

COMBO
The COMBO widget is initialized like this:
Widget_handle_ = COMBO( "first item", size-x, size-y )

Items are added to the COMBO widget with the TEXT method:
TEXT( Widget_handle_, "2nd item" )
TEXT( Widget_handle_, "3rd item" )
TEXT( Widget_handle_, "4th item" )

Then the COMBO is added to the widow in the same way the buttons
were in the previous example.
ATTACH(Mainwin_, Widget_handle_, x-coord, y-coord )

rod

Code: Select all

' BaCon / HUG  Hello BaCon (World)
' Add a Combo widget (drop down)

INCLUDE "/usr/share/BaCon/hug_imports.bac"
INIT

GLOBAL Mainwin_, Menu_label_, Drink_label_, Drink_combo_, My_close_ TYPE int

' ******************
' SUBS & FUNCTIONS
' ******************

' ------------------
SUB MAKE_GUI
' ------------------
	Mainwin_ = WINDOW( "Hello BaCon", 400, 400 )

	Menu_label_ = MARK( "Please choose items from Menu", 200, 28 )
	ATTACH( Mainwin_, Menu_label_, 100, 0 )

	Drink_label_ = MARK("Select Drink", 100, 28)
	ATTACH( Mainwin_, Drink_label_, 10, 25)

	'--- Add a COMBO widget with 4 items
	Drink_combo_ = COMBO("Coffee", 100, 28 )
	TEXT( Drink_combo_, "Tea")
	TEXT( Drink_combo_, "Milk")
	TEXT( Drink_combo_, "O,J.")
	ATTACH( Mainwin_, Drink_combo_, 10, 50 )


	'--- BUTTONS ---
	Close_btn_ = STOCK("gtk-close", 100, 28)
	ATTACH(Mainwin_, Close_btn_, 290, 365)

	'--- CALLBACKS ---
	CALLBACK(Close_btn_, QUIT)
END SUB

' ******************
' END SUBS & FUNCTIONS
' ******************


' ******************
' MAIN PROGRAM
' ******************

MAKE_GUI

DISPLAY
Attachments
hello_bacon.png
(9.68 KiB) Downloaded 1396 times
hello_bacon_combo.tar.gz
source file
(641 Bytes) Downloaded 596 times

User avatar
sunburnt
Posts: 5090
Joined: Wed 08 Jun 2005, 23:11
Location: Arizona, U.S.A.

#33 Post by sunburnt »

Hey GatorDog; Thanks for the help at the BaCon forum, mucho appreciato!

Thoughts; I dislike the underscore "_", after years of Visual Basic I like the period.

And the Visual Basic convention for control naming, "winMain" or "btnOk".
The lower case first part ids the control type, the next part ids it`s purpose.
This is very sensible and easy to understand.

I liked your idea of using the underscore at the end of handles, it`s like a little handle.
So the handle for the button control "btnOk" would be "btnOk_".

Qs ( of course ).
Can a variable list be used for the combo items? Example:

Code: Select all

   TEXT( Drink_combo_, "$cboMonths_list") 
So there would be only one TEXT statement for the combo box.
I`d hope there`s a method of doing this some way.

Keep the tutorials coming GatorDog, I think Barry`s behind you.

User avatar
GatorDog
Posts: 138
Joined: Tue 12 Sep 2006, 16:43

#34 Post by GatorDog »

Can a variable list be used for the combo items? Example:
Code:
TEXT( Drink_combo_, "$cboMonths_list")

So there would be only one TEXT statement for the combo box.
I`d hope there`s a method of doing this some way.
In pFontSelect (earlier post) I populated the list of fonts with a FOR/NEXT loop.
So if the list of items are already in Some_array$[] of length Number_of_items, then
you can add these items to the list or combo with something like this:
(assuming you have OPTION BASE 1 set up, you can start count with 1)

Code: Select all

FOR Count = 1 to Number_of_items
	TEXT( Drink_combo_, Some_array$[Count] )
NEXT
If you don't have an array$[] yet, but have the items in a string, say seperated by ":",
ex Drink$ = "Coffee:Tea:Milk:Water:O.J."
Then use the SPLIT command to create an array, then populate the list/combo.

Code: Select all

SPLIT Drink$ BY ":" TO Drink_array$ SIZE Drink_Count
FOR Count = 1 to Drink_count
	TEXT( Drink_combo_, Drink_array$[Count] )
NEXT

User avatar
GatorDog
Posts: 138
Joined: Tue 12 Sep 2006, 16:43

Hello Bacon (World)

#35 Post by GatorDog »

NEW -

FRAME widget
The FRAME widget lets you visually group items together.
Here we'll use it to group the radio buttons. First
give the FRAME a Handle_ and size. Optionally give the
frame a title with the TEXT command. Then attach it to
our main window.



RADIO buttons widget
The RADIO buttons are usually used when you need the
user to select one of several options. The group name
assigns the radio button to a specific group of buttons.
See [html=http://www.basic-converter.org/hugdoc.html]HUG[/html] doc for further information about group name.
Handle_1_ = RADIO( "First item", x-size, y-size, group_name )
Handle_2_ = RADIO( "Second item", x-size, y-size, group_name )
Handle_3_ = RADIO( "Third item", x-size, y-size, group_name )


SET method
Use the SET method to "set" the default to the third radio button.
SET( Handle_3_, TRUE )

TRUE and FALSE are reserved keywords.
Basically FALSE is defined as "0" zero, and TRUE anything other than
zero, but you'll probably find that TRUE is "1".

Here is the code to add the FRAME and RADIO buttons. The download has
the complete program so far.

rod

Code: Select all

	'--- Add a FRAME for slice size
	Slice_frame_ = FRAME( 265, 50 )
	TEXT( Slice_frame_, " Slice My Bacon: " )
	ATTACH( Mainwin_, Slice_frame_, 120, 32 )

	'--- Add RADIO buttons for slice size selection
	Slice_size_1_ = RADIO( "Thin"  , 70, 28, Slice_size_1_ )
	Slice_size_2_ = RADIO( "Medium", 70, 28, Slice_size_1_ )
	Slice_size_3_ = RADIO( "Thick" , 70, 28, Slice_size_1_ )
	ATTACH( Mainwin_, Slice_size_1_, 130, 50 )
	ATTACH( Mainwin_, Slice_size_2_, 220, 50 )
	ATTACH( Mainwin_, Slice_size_3_, 310, 50 )
	SET( Slice_size_3_, TRUE )

Attachments
hello_bacon.png
(11.66 KiB) Downloaded 1043 times
hello_bacon-frame.tar.gz
source
(788 Bytes) Downloaded 587 times

User avatar
sunburnt
Posts: 5090
Joined: Wed 08 Jun 2005, 23:11
Location: Arizona, U.S.A.

#36 Post by sunburnt »

Thanks GatorDog; Would you prefer that I ask Qs in another thread or maybe at the BaCon forum?

Q; You ATTACH the radio buttons to MainWin, not the frame.
So I assume the frame is just a visual widget and not a widget container?

User avatar
GatorDog
Posts: 138
Joined: Tue 12 Sep 2006, 16:43

#37 Post by GatorDog »

Q; You ATTACH the radio buttons to MainWin, not the frame.
So I assume the frame is just a visual widget and not a widget container?
I just tried a quick test. It does not seem to be a container. But if push comes to
shove, it might be best to get it straight from Peter :)

rod

User avatar
sunburnt
Posts: 5090
Joined: Wed 08 Jun 2005, 23:11
Location: Arizona, U.S.A.

#38 Post by sunburnt »

I tested it using the frame`s handle and it doesn`t show the widget.
Changing the X, Y position I placed the radio button half over the frame edge.
That settles it, the frame is not a widget container, it`s just a visual widget.

User avatar
sunburnt
Posts: 5090
Joined: Wed 08 Jun 2005, 23:11
Location: Arizona, U.S.A.

#39 Post by sunburnt »

I`m working with a combo and I wondered, how to get the item selected?
Need to do this both by making a combo selection and also by a button.

Need to modify my HUG library so the combo will use an array.

User avatar
GatorDog
Posts: 138
Joined: Tue 12 Sep 2006, 16:43

#40 Post by GatorDog »

I`m working with a combo and I wondered, how to get the item selected?
Need to do this both by making a combo selection and also by a button.
If you mean you want to get the currently selected COMBO item, do that with GRAB$.

Code: Select all

Picked_item$ = GRAB$( Combo_widget_ )
If you mean that you want to set the default value, use the SET method
SET( Combo_widget_ , 3) . To set it with a button, do a callback to a
function/sub that then does a SET. :wink:

From HUG doc:
SET(widget, value)
"Depending on the widget, sets a value into the widget. The current behavior is shown in the table below."
Combo - Select entry in combo

rod

User avatar
sunburnt
Posts: 5090
Joined: Wed 08 Jun 2005, 23:11
Location: Arizona, U.S.A.

#41 Post by sunburnt »

So then I assume the combo will use the callback event?
It`s the only way I can think of to know when a selection is made.
The button callback event can use the GRAB method of course.

I think I`m going to make a complete set of widget test files.
Then it`ll be easy to try out all the different HUG possibilities.

User avatar
GatorDog
Posts: 138
Joined: Tue 12 Sep 2006, 16:43

Hello Bacon (World)

#42 Post by GatorDog »

For both of you who are following along, I hope you're
trying out these examples. Make changes, move things around,
add to, etc.

A "CONST" (constant) is a variable that is not going to change value
during the course of the program run. I want a carriage return
ie. NewLine in the text so that the text for the CHECKbox
isn't stretched out. We'll use the CONCAT$ command to
concatinate (string together) three string$. "NL$" is
a reserved word for the "New Line" character. CONST doesn't
really buy us much in a program of this size. But in larger
programs it'll help us keep track of variable names we're using
and save memory space. (This is my opinion/thought on CONST).
Since the CONST declares a variable of fixed length, it is static.
The program sets it up and leaves it alone. A variable length
variable can be dynamic, changing length, memory location and
how much memory it occupies. That means the program has to spend
some amount of resources keeping track of it, moving it around,
making sure there is memory available. etc. So I suggest using
CONST where appropriate. For GLOBAL variables, in addition to
helping you keep track of the variables you're using, it gives
the compiler a "heads up" on the variables to come.
(Or I may just be full of it ;) ) At any rate, we'll use this
CONST in our program. Look at the screen shot to see the effects of NL$.

Code: Select all

CONST Egg_checkbox$ = CONCAT$("Check if you", NL$, " want eggs")
Introducing three more widgets AND going to add a tad bit of logic in to boot.

We'll add a section for ordering Eggs, putting a FRAME around it.
Nothing new here:

Code: Select all

	'--- Add FRAME for eggs
	Eggs_frame_ = FRAME(380, 130)
	TEXT(Eggs_frame_, " How about those Eggs ")
	ATTACH(Mainwin_, Eggs_frame_, 10, 85)
The first widget is the CHECKbox.

Code: Select all

	'--- CHECK box for eggs
	Eggs_or_not_ = CHECK(Egg_checkbox$, 100, 45)
	ATTACH(Mainwin_, Eggs_or_not_, 20, 130)
Next up is a LIST widget. Items are added to the LIST widget
in the same way we added items to the COMBO widget; with
the TEXT method. I deliberately added more items to the
list than will fit in the LIST window. This to show that
the list window will automatically add a scrool bar. If
an item in the list is too wide to fit the window, a
horizontal scrool bar will be added. Note the DISABLE
method is used, the list is "grayed out" and not accessable.
(We'll do the same for the egg count).

Code: Select all

	'--- Add a LIST widget for cooking eggs
	Eggs_list_ = LIST(120, 100)
	TEXT(Eggs_list_, "Sunny Side Up")
	TEXT(Eggs_list_, "Over Easy")
	TEXT(Eggs_list_, "Scrambled")
	TEXT(Eggs_list_, "Poached")
	TEXT(Eggs_list_, "Runny")
	TEXT(Eggs_list_, "Hard Boiled")
	ATTACH(Mainwin_, Eggs_list_, 150, 105)
	DISABLE(Eggs_list_)
The SPIN widget is just an up/down counter. It's set up
like this:
Handle_ = SPIN( xsize, ysize, start_count, end_count, step)
Throw in a label so the user knows what the count is for.
This SPINner allows the user to pick from 1 to 4 eggs.

Code: Select all

	'--- Label/MARK for egg count
	Tmp$ = CONCAT$("How Many", NL$, "    Eggs")
	How_many_label_ = MARK(Tmp$, 75, 40)
	ATTACH(Mainwin_, How_many_label_, 300, 120)

	'--- Add SPINner widget for how many eggs
	How_many_eggs_ = SPIN(40, 26, 1, 4, 1)
	ATTACH(Mainwin_, How_many_eggs_, 320, 160)
	DISABLE(How_many_eggs_)
	SET(How_many_eggs_, 2)
Now we'll add in some logic. If the CHECK box is blank, then
the user doesn't get to make selections about the eggs.
ie. DISABLE'd. If the box is checked, we need to ENABLE the
egg selection widgets. We'll do the actual enable-ing with a
call to a SUBroutine; get to that in a moment.

There are two parts to the logic here. First, watch the
CHECK box for a change. Second, do something if the CHECK box
status changes. We'll set up a CALLBACK function to watch for
an "event" (in this case anything) to happen to the CHECK box.
We gave the CHECK box the widget handle "Eggs_or_not_". And
we'll write a subroutine named "ENABLE_EGGS" to execute when
an event occurs to "Eggs_or_not_".

Code: Select all

CALLBACK(Eggs_or_not_, ENABLE_EGGS)
About the IF/THEN construct. In simplest form-

Code: Select all

IF this_is_true THEN
	do_this
END IF
The "this_is_true" just needs to evaluate to a non zero
result. In our case, GET(.....) gets the value of the CHECK box
widget. "0" if not checked, "1" if checked. So if our CHECK box
is checked, GET(Eggs_or_not_) evaluates to "1" ie TRUE, and the
following commands are executed. Otherwise it must be FALSE, so
execute the commands after the "ELSE" statement. For more in-depth
info on IF/THEN, Google-it.

Code: Select all

' ------------------
SUB ENABLE_EGGS
' ------------------
	IF GET(Eggs_or_not_) THEN
		ENABLE(Eggs_list_)
		ENABLE(How_many_eggs_)
	ELSE
		DISABLE(Eggs_list_)
		DISABLE(How_many_eggs_)
	END IF
END SUB
Check out the whole program to see how the widgets are used, and the
CALLBACK and SUB routine are setup.

rod

(how did I get the text at the top bigger and thicker ?)
Attachments
hello_bacon.png
(26.94 KiB) Downloaded 1009 times
hello_bacon.tar.gz
hello_bacon source
(1.24 KiB) Downloaded 603 times

User avatar
sunburnt
Posts: 5090
Joined: Wed 08 Jun 2005, 23:11
Location: Arizona, U.S.A.

#43 Post by sunburnt »

Rob; Next make the combo auto put the selection into a entry.
And add a button that puts the combo`s current selection into a label.

So combo action is demoed, and changing a label`s text is also.

Combo boxes are a real sore point of gtkDialog. Little functionality.

User avatar
GatorDog
Posts: 138
Joined: Tue 12 Sep 2006, 16:43

Combo Challenge

#44 Post by GatorDog »

Next make the combo auto put the selection into a entry.
And add a button that puts the combo`s current selection into a label.
Okie Dokie :wink:
rod

Code: Select all

'Da combo challenge
INCLUDE "/usr/share/BaCon/hug_imports.bac"
INIT

' ------------------
FUNCTION SEND_TEXT_TO_ENTRY
' ------------------
	TEXT(My_entry_, GRAB$(My_combo_))
	RETURN TRUE
END FUNCTION

' ------------------
SUB COMBO2LABEL
' ------------------
	TEXT(My_label_, GRAB$(My_combo_))
END SUB

' ------------------
SUB CLEAR_LABEL
' ------------------
	TEXT(My_label_, "")
END SUB

' ------------------
SUB MAKE_GUI
' ------------------
	Mainwin_ = WINDOW( "Combo Challenge", 200, 200 )
	PROPERTY(Mainwin_, "icon-name", "gtk-execute")

	'--- COMBO widget with 3 items
	My_combo_ = COMBO("November", 100, 28 )
	TEXT( My_combo_, "Whiskey")
	TEXT( My_combo_, "India")
	ATTACH( Mainwin_, My_combo_, 50, 10 )

	My_entry_ = ENTRY("", 100, 30)
	ATTACH(Mainwin_, My_entry_, 50, 40)

	My_frame_ = FRAME(100, 40)
	TEXT(My_frame_, " Label in here")
	FONT(My_frame_, " 9")
	ATTACH(Mainwin_, My_frame_, 50, 75)

	My_label_ = MARK("", 100, 30)
	ATTACH(Mainwin_, My_label_, 50, 80)

	My_btn_ = BUTTON("Combo2Label", 100, 30)
	ATTACH(Mainwin_, My_btn_, 50, 120)

	My_other_btn_ = BUTTON("Clear-Label", 100, 30)
	ATTACH(Mainwin_, My_other_btn_, 50, 160)

	CALLBACK(My_btn_, COMBO2LABEL)
	CALLBACK(My_other_btn_, CLEAR_LABEL)
	TIMEOUT(500, SEND_TEXT_TO_ENTRY)

END SUB

' ****************
'  MAIN PROGRAM
' ****************

MAKE_GUI

DISPLAY
Attachments
combo_challenge.jpg
(8.73 KiB) Downloaded 1090 times

User avatar
sunburnt
Posts: 5090
Joined: Wed 08 Jun 2005, 23:11
Location: Arizona, U.S.A.

#45 Post by sunburnt »

Very nice GatorDog, it took a minute for me to see the TIMEOUT.

I was puzzled by what the action was that made the combo work.

User avatar
GatorDog
Posts: 138
Joined: Tue 12 Sep 2006, 16:43

#46 Post by GatorDog »

We'll add another widget to Hello BaCon and then look
into processing the information gathered.

Concept- If we want to count out 5 apples, we don't call the
first apple zero. However, computers like to start counting at zero.
(We don't). Bacon has an option that specifies to start counts with
1 instead of zero. "OPTION BASE 1". Put that near the top of the
program. See the BaCon doc for further information.

The ENTRY widget allows the user to type in some piece
of information. Their name for example. This is how we'll
set up an info label and an ENTRY widget.

Code: Select all

	'--- Customer info
	Customer_label_ = MARK("We'll call you when order is ready!", 380, 30)
	PROPERTY(Customer_label_, "xalign", 0.5)
	ATTACH(Mainwin_, Customer_label_, 10, 220)

	Customer_name_ = ENTRY(Customer_directions$, 380, 30)
	ATTACH(Mainwin_, Customer_name_, 10, 246)
To initiate the process I've added a "Place-Order" button next to
the "Cancel" button. We need a CALLBACK to trigger a FUNCTION
when the "Place-Order" button is clicked.

Code: Select all

	Place_order_ = BUTTON("Place-Order", 100, 28)
	ATTACH(Mainwin_, Place_order_, 170, 365)

	CALLBACK(Place_order_, PLACE_ORDER)
Now to write a function named FUNCTION PLACE_ORDER.
We'll only do minimal amount of error checking.
GRAB$(widget_) gets the text, if any, that is in the ENTRY widget.
The double set of double quotes stands for a "null" string (ie. nothing).
If ENTRY is blank, we'll just wait until the customer
is ready to let us know their name ;) The CHOP$ command removes any
leading or trailing spaces or tabs.

Code: Select all

' ------------------
FUNCTION PLACE_ORDER
' ------------------
	Customer_name$ = GRAB$(Customer_name_)
	Customer_name$ = CHOP$(Customer_name$)
	IF Customer_name$ = "" THEN
		RETURN FALSE
	END IF
END FUNCTION
Next we'll check to see if there are spaces in the name. Maybe they
entered their first and last names. To do this, we use the SPLIT
command. We use the space character as the seperator (if there is one).
ex. SPLIT String_to_split$ BY character_to_split_on TO an_array_name$ SIZE how_many_names.
After SPLIT seperates the string into individual smaller chunks (array), it sets
the "how_many_names" variable to the number of smaller chunks. If the customer
entered their first and last name, how_many_names will be set to 2. If there is only
one name, SPLIT is happy setting how_many to one. By the way, if we hadn't used
the "OPTION BASE 1" mentioned earlier, we'd be working with 0 and 1. One meaning
that there are actually two items. (That can get confusing quick.) Any way,
using SPLIT we can make sure we have a single name and that's all the error
checking we'll do on that item.

Code: Select all

' ------------------
FUNCTION PLACE_ORDER
' ------------------
	Customer_name$ = GRAB$(Customer_name_)
	IF Customer_name$ = "" THEN
		RETURN FALSE
	END IF

	SPLIT Customer_name$ BY " " TO Array$ SIZE How_many_names
	Customer_name$ = Array$[ How_many_names ]
END FUNCTION
Refer to the following function code for the rest of this discussion.
Grab the drink order with Drink$ = GRAB$(Drink_combo_) .

The radio buttons for slice thickness take a little more work. We have
to check each one to see which is selected. It will be TRUE if it is selected,
otherwise FALSE. This is a simple IF/THEN test so we can use the shortened IF/THEN syntax.

Now check if they're ordering eggs, and if so grab that information.
The call to our SHOW_ORDER function opens a message box so that the customer
can confirm the order is correct.

Code: Select all

' ------------------
FUNCTION PLACE_ORDER
' ------------------
	Customer_name$ = GRAB$(Customer_name_)
	IF Customer_name$ = "" THEN
		RETURN FALSE
	END IF

	SPLIT Customer_name$ BY " " TO Array$ SIZE How_many_names
	Customer_name$ = Array$[ How_many_names ]

	Drink$ = GRAB$(Drink_combo_)

	IF GET(Slice_size_1_) THEN Slice_thickness$ = "Thin"
	IF GET(Slice_size_2_) THEN Slice_thickness$ = "Medium"
	IF GET(Slice_size_3_) THEN Slice_thickness$ = "Thick"

	Eggs_cooked$ = ""
	How_many_eggs$ = ""
	IF GET(Eggs_or_not_) THEN
		Eggs_cooked$ = GRAB$(Eggs_list_)
		How_many_eggs$ = STR$( GET(How_many_eggs_))
	END IF
	
	SHOW_ORDER : 'Show Msgdialog, Confirm order
	RETURN TRUE
END FUNCTION
This is the code added to set up a MSGDIALOG. Refer to the complete
source code for location and details.

Code: Select all

	'--- This initializes the MSGDIALOG, but doesn't display it yet
	Order_window_ = MSGDIALOG("Confirm Order", 300, 220, 4, 5)

	'--- This "shows" the MSGDIALOG when the "Place-Order" button is clicked
	CALLBACK(Place_order_, PLACE_ORDER)

	'--- This "hides" the MSGDIALOG when the "dialog" button is clicked
	CALLBACK(Order_window_, ORDER_CLOSE)
These are the subroutines that open and close the msgdialog widget.

Code: Select all

' ------------------
SUB SHOW_ORDER
' ------------------
	Tmp$ = CONCAT$("Hello ", Customer_name$, NL$, NL$)
	Tmp$ = CONCAT$(Tmp$, "Please click OK if order is correct", NL$, NL$)
	Tmp$ = CONCAT$(Tmp$, "Drink - ", Drink$, NL$)
	Tmp$ = CONCAT$(Tmp$, "Bacon - ", Slice_thickness$, NL$)
	IF GET(Eggs_or_not_) THEN
		Tmp$ = CONCAT$(Tmp$, How_many_eggs$, " Egg(s), ", Eggs_cooked$, NL$)
	ELSE
		Tmp$ = CONCAT$(Tmp$, "No Eggs", NL$)
	END IF

	TEXT(Order_window_, Tmp$)
	SHOW(Order_window_)
END SUB

' ------------------
SUB ORDER_CLOSE(NUMBER Dialog_, int Button_)
' ------------------
	HIDE(Dialog_)
END SUB
This Hello BaCon program should give you a good running start to create GUI's
using BaCon and HUG.

I added a few other goodies in the source to discover, but you should be able
to figure them out :P

Happy Crankin'
rod
Attachments
hello_bacon.tar.gz
source
(2.11 KiB) Downloaded 579 times
hello_bacon.jpg
(25.93 KiB) Downloaded 1092 times

User avatar
vovchik
Posts: 1507
Joined: Tue 24 Oct 2006, 00:02
Location: Ukraine

bacon diner

#47 Post by vovchik »

Dear Rod,

Thanks for the nice demo - I think it will help people... I think Peter should also post it at his site.

With kind regards,
vovchik

User avatar
GatorDog
Posts: 138
Joined: Tue 12 Sep 2006, 16:43

BaCon File I/O

#48 Post by GatorDog »

Sooner or later you'll write a program that collects some data that you'd
like to save for later use. That's where File I/O - Input/Output comes in.
The general flow is -
OPEN file
WRITE data
CLOSE file
There are variations on this. For example putting the WRITE data in a loop
to write several data samples. Reading the data back in is -
OPEN file
READ data
CLOSE file
Again with some variations in setup.

BaCon has several directives for File I/O. Here I'll cover plain ASCII
reading and writting which uses the READING, WRITING, APPENDING directives.
READING is for reading in data from a file. WRITING and APPENDING both
write to a file. The difference being that WRITING will overwrite an
existing file with the new data. APPENDING adds the data to the end of
an existing file. Here I'll be using READING and WRITING.

First let's create a gui that asks for three pieces of data, and a button.
(The button doesn't do anything yet.)

Code: Select all

'BaCon File I/O

INCLUDE "/usr/share/BaCon/hug_imports.bac"
INIT

HUGOPTIONS("BASEXFTDPI 78")
HUGOPTIONS("FONT DejaVu Sans 12")

OPTION BASE 1
CONST Mainwin_width = 300
CONST Mainwin_height = 250
CONST Mainwin_border = 5
CONST Widget_width = Mainwin_width - (2 * Mainwin_border)
CONST Widget_height = 30
CONST Widget_spacing = 5
CONST Btn_height = 25
CONST Btn_width = 90

GLOBAL Row TYPE int

' ------------------
SUB MAKE_GUI
' ------------------
	Mainwin_ = WINDOW("File I/O", Mainwin_width, Mainwin_height)

	Label1_ = MARK("First & Last Name:", Widget_width, Widget_height)
	Entry1_ = ENTRY("", Widget_width, Widget_height)

	Label2_ = MARK("Age:", Widget_width, Widget_height)
	Entry2_ = ENTRY("", Widget_width, Widget_height)

	Label3_ = MARK("Yes or No:", Widget_width, Widget_height)
	Entry3_ = ENTRY("", Widget_width, Widget_height)

	Row = 0
	ATTACH(Mainwin_, Label1_, Mainwin_border, Row)
	INCR Row, Widget_height + Widget_spacing
	ATTACH(Mainwin_, Entry1_, Mainwin_border, Row)

	INCR Row, Widget_height + Widget_spacing
	ATTACH(Mainwin_, Label2_, Mainwin_border, Row)
	INCR Row, Widget_height + Widget_spacing
	ATTACH(Mainwin_, Entry2_, Mainwin_border, Row)

	INCR Row, Widget_height + Widget_spacing
	ATTACH(Mainwin_, Label3_, Mainwin_border, Row)
	INCR Row, Widget_height + Widget_spacing
	ATTACH(Mainwin_, Entry3_, Mainwin_border, Row)

	'--- Bottom Buttons
	Save_btn_ = BUTTON("Save", Btn_width, Btn_height)
	Btn_x = Mainwin_width - Btn_width - Mainwin_border
	Btn_y = Mainwin_height - Btn_height - Mainwin_border
	ATTACH(Mainwin_, Save_btn_, Btn_x, Btn_y)

END SUB

MAKE_GUI

DISPLAY
Use CONST and variables wherever you can for dimentioning and placing
widgets. It allows you to change your gui with a single edit.
The above code creates our main gui window. Then three labels and three
ENTRY fields. Then the labels and entry (widgets) are attached to the main
window. The button is also created and attached to the bottom of the window.

Our save file will need a name. This can be done with the FILEDIALOG widget
but for now we'll just hard code it to save the file in our HOME directory.
We'll add this to our CONST's -

Code: Select all

CONST Filename$ = CONCAT$( GETENVIRON$("HOME"), "/my_file.txt")
Now we'll add a SUBroutine to write the data to the file (my_file.txt", and a
CALLBACK that runs the SUB when the "Save" button is clicked.

The subroutine name is SAVE_TO_FILE. We'll put this above the MAKE_GUI subroutine.

Code: Select all

' ------------------
SUB SAVE_TO_FILE
' ------------------
	OPEN Filename$ FOR WRITING AS Filehandle_
		WRITELN GRAB$(Entry1_) TO Filehandle_
		WRITELN GRAB$(Entry2_) TO Filehandle_
		WRITELN GRAB$(Entry3_) TO Filehandle_
	CLOSE FILE Filehandle_
END SUB
We use GRAB$ to get the text from the entries.

Add the callback to the end of the MAKE_GUI sub.

Code: Select all

CALLBACK(Save_btn_, SAVE_TO_FILE)
Compile and run the program. Fill in the blanks and click Save. The program
saves the data. Check you home folder for the file if you like.

Let's add a button, callback and subroutine to clear or reset the entry fields.
The button and callback go to the end of MAKE_GUI and the SUB CLEAR_ENTRIES above
the MAKE_GUI sub.

Code: Select all

' ------------------
SUB CLEAR_ENTRIES
' ------------------
	TEXT(Entry1_, "")
	TEXT(Entry2_, "")
	TEXT(Entry3_, "")
END SUB

Code: Select all

	Clear_btn_ = STOCK("gtk-clear", Btn_width, Btn_height)
	Btn_x = Btn_x - Btn_width - Widget_spacing
	ATTACH(Mainwin_, Clear_btn_, Btn_x, Btn_y)

	CALLBACK(Clear_btn_, CLEAR_ENTRIES)
Compile and run. Enter some data into the blanks. Click "Clear", the data
will erase.

Now we'll add a button, callback and subroutine to "read" the data from the
file, and populate the entries in the gui. Again, button and callback to the
end of MAKE_GUI and the subroutine above MAKE_GUI.

Code: Select all

' ------------------
SUB READ_FROM_FILE
' ------------------
	LOCAL Name$, Age$, Yes$ TYPE STRING
	OPEN Filename$ FOR READING AS Filehandle_
		READLN Name$ FROM Filehandle_
		READLN Age$  FROM Filehandle_
		READLN Yes$  FROM Filehandle_
	CLOSE FILE Filehandle_
	TEXT(Entry1_, Name$)
	TEXT(Entry2_, Age$)
	TEXT(Entry3_, Yes$)
END SUB

Code: Select all

	Read_btn_ = BUTTON("Read Data", Btn_width, Btn_height)
	Btn_x = Btn_x - Btn_width - Widget_spacing
	ATTACH(Mainwin_, Clear_btn_, Btn_x, Btn_y)

	CALLBACK(Read_btn_, READ_DATA)
Compile and run. Now you can enter and save data. Clear the data fields and
read and display previously saved data.

This is pretty much Straight Up no frills File I/O. For saving and reading a
few pieces of data, it works fine. For a larger number of fields, a loop of
some sort will make more sense.

*Note: All ENTRY fields are text or string data. That means that even numbers are
GRABed from and ENTRY and treated as text or string data. However! Your program
can write a "number" to a file. But! when you read it in, you read it in as a
string variable and if necessary convert it back to a number with VAL(Var$).

rod
Attachments
file-io.png
(14.63 KiB) Downloaded 1078 times
file_io.tar.gz
file i/o source
(938 Bytes) Downloaded 562 times

Volhout
Posts: 547
Joined: Sun 28 Dec 2008, 08:41

#49 Post by Volhout »

I am trying to write a BaCon glue layer to the FTDI (USB serial port) chip. I plan to use this chip in bitbang mode to get 8 individual IO pins.

I managed to connect to the chip, to write data out, and to read data back. Problem is that each write or read takes 1 msecond.
I want to test if sending out multiple bytes in 1 write command achieves a faster throughput. The C-code header file allows this.

test program:

'-------------------------------------------------------------
'
' program to test the driver glue layer in ftdi_drv.bac
'
' 2011-09-02 Harm de Leeuw
'--------------------------------------------------------------

INCLUDE "ftdi_drv.bac"

LOCAL data TYPE unsigned char
LOCAL mask TYPE unsigned char

PRINT "connect to USB module with FT245BM"
mask = 0xFF
ft245bm_open (mask)
PRINT "device connected, enabling bitbang mode"

'PRINT "start banging"
'FOR data = 0 TO 254
' ft245bm_poke (data)
' PRINT RIGHT$(HEX$(ft245bm_peek()),2)
'NEXT data
'PRINT "stop banging, disable bitbang mode"

'trigger
ft245bm_poke (0xFF)
ft245bm_poke (0x00)
ft245bm_poke (0xFF)

'test
PRINT "start printing string"
g$="2"
ft245bm_print (g$)
PRINT "done, closing down"

INPUT a

ft245bm_close ()

END





My glue layer -------------------------------------------------------

'---------------------------------------------------------------------------
' BaCon driver for FTDI USB-SERIAL convertor chips. Uses compiled libraries
' from FTDI : libftdi-0.19. When compiled (./configure, make, make install)
' this library generates libftdi.so.1
' 2011-09-02 Harm de Leeuw
'---------------------------------------------------------------------------
' Version
' 0.1 Supports open(mask), poke(byte), and close()
' 0.2 Added peek() function
'---------------------------------------------------------------------------
TRAP LOCAL

' Import some calls first
CONST library$ = "libftdi.so.1"

' Get the functions from the library
IMPORT ftdi_init(long) FROM library$ TYPE int
IMPORT ftdi_usb_open(long,int,int) FROM library$ TYPE int
IMPORT ftdi_get_error_string(long) FROM library$ TYPE char* ALIAS ftdi_get_error_string$
IMPORT ftdi_read_chipid(long,long) FROM library$ TYPE int
IMPORT ftdi_usb_close(long) FROM library$ TYPE int
IMPORT ftdi_deinit(long) FROM library$ TYPE void
IMPORT ftdi_write_data(long,long,int) FROM library$ TYPE int
IMPORT ftdi_set_bitmode(long,unsigned char,unsigned char) FROM library$ TYPE int
IMPORT ftdi_disable_bitbang(long) FROM library$ TYPE int
IMPORT ftdi_usb_get_strings(long,long,long,int,long,int,long,int) FROM library$ TYPE int
IMPORT ftdi_read_pins(long,long) FROM library$ TYPE int

CONST TYPE_AM = 0
CONST TYPE_BM = 1
CONST TYPE_2232C = 2
CONST TYPE_R = 3
CONST TYPE_2232H = 4
CONST TYPE_4232H = 5

CONST BITMODE = 0x01

' BaCon does not know types so we define
' a RECORD with the needed members
RECORD ftdic
' USB specific
LOCAL usb_dev TYPE long
LOCAL usb_read_timeout TYPE int
LOCAL usb_write_timeout TYPE int
' FTDI specific
LOCAL type TYPE int
LOCAL baudrate TYPE int
LOCAL bitbang_enabled TYPE unsigned char
LOCAL *readbuffer TYPE unsigned char
LOCAL readbuffer_offset TYPE unsigned int
LOCAL readbuffer_remaining TYPE unsigned int
LOCAL readbuffer_chunksize TYPE unsigned int
LOCAL writebuffer_chunksize TYPE unsigned int
LOCAL max_packet_size TYPE unsigned int
' FTDI FT2232C requirecments
LOCAL interface TYPE int
LOCAL index TYPE int
' Endpoints
LOCAL in_ep TYPE int
LOCAL out_ep TYPE int
' General
LOCAL bitbang_mode TYPE unsigned char
LOCAL eeprom_size TYPE int
LOCAL error_str TYPE char*
LOCAL async_usb_buffer TYPE char*
LOCAL async_usb_buffer_size TYPE unsigned int
END RECORD

'-----------------------------------------------------------------------
SUB ft245bm_open (unsigned char io_mask)
'-----------------------------------------------------------------------
' this function initializes the FTDI 245 BM chip attached to the USB bus
' of the PC in bitbang mode (8 bit wide IO port). The io_mask define the
' data direction ("1"=out, "0"=in).
' timing: every byte write takes 1 milisecond, regardless CPU speed.

IF ftdi_init(ADDRESS(ftdic)) < 0 THEN
PRINT "ftdi_init failed"
END 1
END IF
ret = ftdi_usb_open(ADDRESS(ftdic), 0x0403, 0x6001)
IF ret < 0 THEN
PRINT "unable to open ftdi device: ", ret, " (", ftdi_get_error_string$(ADDRESS(ftdic)), ")"
END 1
END IF
ret = ftdi_set_bitmode(ADDRESS(ftdic), io_mask, BITMODE)
IF ret < 0 THEN
PRINT "unable to set bitmode: ", ret, " (", ftdi_get_error_string$(ADDRESS(ftdic)), ")"
END 1
END IF
END SUB

'-----------------------------------------------------------------------
SUB ft245bm_poke (unsigned char data)
'-----------------------------------------------------------------------
' this function sends out a byte to the 8 bit wide io port of the ftdi
' 245bm chip. The actual data output is masked by the io_mask set in
' the init command.
ret = ftdi_write_data(ADDRESS(ftdic), ADDRESS(data), 1)
IF ret < 0 THEN
PRINT "unable to write: ", ret, " (", ftdi_get_error_string$(ADDRESS(ftdic)), ")"
END 1
END IF
END SUB


'-----------------------------------------------------------------------
SUB ft245bm_close ()
'-----------------------------------------------------------------------
' this function disconnects the ftdi 245bm chip from the USB bus.
' first bitbang mode is disabled, putting the io pins in tristate.
ret = ftdi_disable_bitbang(ADDRESS(ftdic))
IF ret < 0 THEN
PRINT "unable to close ftdi device: ", ret, " (", ftdi_get_error_string$(ADDRESS(ftdic)), ")"
END 1
END IF
ret = ftdi_usb_close(ADDRESS(ftdic))
IF ret < 0 THEN
PRINT "unable to close ftdi device: ", ret, " (", ftdi_get_error_string$(ADDRESS(ftdic)), ")"
END 1
END IF
ftdi_deinit(ADDRESS(ftdic))
END SUB

'-----------------------------------------------------------------------
FUNCTION ft245bm_peek () TYPE unsigned char
'-----------------------------------------------------------------------
' this function reads the 8 io pins from the ftdi 245bm chip, and returns
' the value. The value read is depending on the mask set when opening the
' connection.
LOCAL data TYPE unsigned char
ret = ftdi_read_pins(ADDRESS(ftdic), ADDRESS(data))
IF ret < 0 THEN
PRINT "unable to read: ", ret, " (", ftdi_get_error_string$(ADDRESS(ftdic)), ")"
END 1
END IF
RETURN data
END FUNCTION

' *** TEST ***
'-----------------------------------------------------------------------
SUB ft245bm_print (STRING data)
'-----------------------------------------------------------------------
' this function sends out a string of bytes byte to the 8 bit wide io
' port of the ftdi ' 245bm chip. The actual data output is masked by
' the io_mask set in the init command by the chip.
LOCAL len TYPE int
len = LEN(data)
PRINT len
PRINT data FORMAT "%s"
ret = ftdi_write_data(ADDRESS(ftdic), data, len)
IF ret < 0 THEN
PRINT "unable to write: ", ret, " (", ftdi_get_error_string$(ADDRESS(ftdic)), ")"
END 1
END IF
END SUB
' *** TEST ***

'-----------------------------------------------------------------------




The void that does not work is ft245bm_print. In the line

ret = ftdi_write_data(ADDRESS(ftdic), data, len)

I get a compiler error that data is converted from pointer to integer. Whereas I defined data as a string. Even if I replace

data

with

ADDRESS(data)

it does not work.

Any idea's ??

User avatar
GatorDog
Posts: 138
Joined: Tue 12 Sep 2006, 16:43

#50 Post by GatorDog »

This does look interesting, and I'd like to be able to do it myself.
Hopefully someone with some experience with this will check in.

rod

Post Reply