go-iup Introduction
Iup is a cross-platform GUI toolkit that wrap platform native widgets, thus always a native and high performance experience for the user yet a simple API for the developer. It contains all the common widgets and in addition an advanced canvas, an OpenGL canvas, web browser widget (embedding Iexplorer on Windows and WebKit on other platforms), pplot and touch screen support.
Go is an open source staticly typed compiled programming language by Google that is expressive, concise, clean and efficient.
What a better mix than Go and Iup? Thus, go-iup is born, the wrapper around Iup for Go.
Before going any futher, let’s look at a simple Hello World program using go-iup:
package main
import "github.com/jcowgar/go-iup"
func main() {
iup.Open()
defer iup.Close()
iup.Message("Greetings", "Hello, World!")
}
Pretty simple, eh? Sure, but it’s only a message box. Not very fun. Instead, let’s disect a real program… Rot 13. Enable the user to enter a message or load a file and either encode or decode the text.
First let’s get the boiler plat code out of the way. This includes a rot 13 algorithm for Go found on Rosetta Code.
package main
import (
"strings"
"io/ioutil"
"github.com/jcowgar/go-iup"
)
func rot13char(c int) int {
if c >= 'a' && c <= 'm' || c >= 'A' && c <= 'M' {
return c + 13
} else if c >= 'n' && c <= 'z' || c >= 'N' && c <= 'Z' {
return c - 13
}
return c
}
func rot13(s string) string {
return strings.Map(rot13char, s)
}
Not much is going on here except for the all important import of go-iup, import "github.com/jcowgar/go-iup"
Let’s start with the main function which will create the GUI, then we will go into the callback functions that do the actual work.
func main() {
iup.Open()
defer iup.Close()
menu := iup.Menu(
iup.Submenu("File",
iup.Menu(
iup.Item("Load File", (iup.ActionFunc)(onLoadFile)),
iup.Item("Rotate", (iup.ActionFunc)(onRotate)),
iup.Item("Quit", (iup.ActionFunc)(onQuit)))),
iup.Submenu("Help",
iup.Menu(
iup.Item("About Iup", (iup.ActionFunc)(onAboutIup)),
iup.Item("About go-iup", (iup.ActionFunc)(onAboutIupGo)))))
text = iup.Text("MULTILINE=YES,EXPAND=YES,WORDWRAP=YES,SIZE=250x100,SCROLLBAR=YES")
mainBox := iup.SetAttrs(iup.Vbox(
iup.Label("Text to be rotated:"),
text,
iup.SetAttrs(iup.Hbox(
iup.Button("Load File", "PADDING=3x3", (iup.ActionFunc)(onLoadFile)),
iup.Button("Rotate", "PADDING=3x3", (iup.ActionFunc)(onRotate)),
iup.Button("Quit", "PADDING=3x3", (iup.ActionFunc)(onQuit)),
), "MARGIN", "0x0"),
), "MARGIN", "5x5", "GAP", "3")
mainDlg = iup.Dialog(mainBox, "TITLE=\"Rot 13\"")
iup.SetAttributeHandle(mainDlg, "MENU", menu)
iup.Show(mainDlg)
iup.MainLoop()
}
I am hoping the above is almost self explanatory, but let’s go over a few things.
iup.Open()
must be called before using any Iup functions.iup.Close()
should be called at the end of your program to clean up after Iup.iup.MainLoop()
enters the GUI event handler.- Iup uses mainly sizers for widget layout. In our example above you’ll see vertical (
Vbox
)
and horizontal (Hbox
) boxes to place widgets into.
Further, one important thing to realize from the above code is that Iup does not have all sorts widget specific functions. Instead it uses the idea of attributes. For example do you want to get the value of a widget? iup.GetAttribute(widget, "VALUE")
. Do you want to tell a text widget that it should be multi-line? iup.SetAttribute(widget, "MULTILINE", "YES")
. There are various attribute setters and getters but these are far more simple than many methods acting on each widget type.
Let’s look at three of the callbacks now, onLoadFile
, onRotate
and onAboutGoIup
.
func onLoadFile(ih *iup.Ihandle) int {
filename, status := iup.GetFile("")
if status == -1 {
return iup.DEFALT
}
content, err := ioutil.ReadFile(filename)
if err != nil {
iup.Message("Error", "Error: "+err.String())
return iup.DEFAULT
}
iup.StoreAttribute(text, "VALUE", string(content))
return iup.DEFAULT
}
func onRotate(ih *iup.Ihandle) int {
content := rot13(iup.GetAttribute(text, "VALUE"))
iup.StoreAttribute(text, "VALUE", string(content))
return iup.DEFAULT
}
func onAboutIupGo(ih *iup.Ihandle) int {
iup.Help("http://github.com/jcowgar/go-iup")
return iup.DEFAULT
}
Most callbacks take as the first parameter the widget being acted upon. They all return an integer value telling Iup what to do next, DEFAULT
, IGNORE
, and CLOSE
are the three big ones.
In the onLoadFile
callback we use a pre-defined dialog that Iup provides to ask the user for a file (standard file open dialog). Iup does have a more advanced iup.FileDlg
dialog that you can customize quite a bit, but no need for it here. Once we have the file name, we simply load it and use good old StoreAttribute
.
The onRotate
callback is just our familiar GetAttribute
and StoreAttribute
calls.
Finally the onAboutIupGo
callback simply calls iup.Help
which will launch the default browser with the supplied URL. Nothing too fancy there.
So… That is a quick introduction to go-iup. I’ve only touched the surface of what is possible. You can bet that more posts will be here on my blog about more advance uses of Iup such as Lua scripting, LED files (text layout specifications), using the plot widget, canvas widget, browser widget, using reflection to auto-populate your dialog or auto-populate your struct from the dialog, etc…
In closing, I want to state the go-iup is still in its infancy. API changes are very likely. All widgets are wrapped and usable. Not all callbacks are accessible. Some more advanced (hardly used) Iup methods are not yet wrapped. Two supporting libraries CD (Canvas Draw) and Im (Image Manipultation) libraries are not wrapped, although not essential to creating advanced GUIs. In short, if you find this project to be of value, I can use your help!
go-iup can be downloaded at: http://github.com/jcowgar/go-iup.
Iup can be downloaded at http://www.tecgraf.puc-rio.br/iup/