External - GUI for Golang

    Greetings, colleagues!

    About a month ago, I published here an article on GUI frameworks — to a stream where technology was proposed for creating GUI frameworks for different programming languages ​​based on connecting (tcp / ip or some other) to an external process playing the role of a kind of GUI server. Here I want to present a concrete implementation of this idea - a new GUI framework for Golang - External .

    Why bother to write a new GUI for Golang?, if you already have a lot of such tools? First of all, because none of them suited me to the full. It was necessary to create desktop applications, cross-platform, so that it looked natural for each platform. If possible, not very cumbersome, having a minimum of dependencies - I am committed to a minimalist approach.

    I was guided by this list . Two positions - the app and walk were immediately crossed out, as not meeting the requirements of cross-platform. After some thought, he rejected those based on html / css / javascript. Firstly, it seems to me somewhat unusual to build a desktop application as a web page and, secondly, they are pulling the heavy engines behind them. For example, ! Go-astilectron and gowd based on Electron and nw.js , respectively, and these, in turn, on node.js . Imagine how much should be installed at the end user to run even a small utility? Is that go-sctiterFrom this point of view, it looks preferable: the Sciter behind it is not so monstrate.

    Go-gtk and gotk3 are based on GTK. This seems to be thoroughly made packages, but I refused them, because, in my opinion, using GTK under Windows is not the best solution. GTK windows do not look native under Windows. Qtbinding is a powerful thing, of course, but rather complicated, and the size ... When I read: "You also need 2.5 GB free RAM (which only needed during initial setup) and at least 5 GB free disk space", the last doubts have disappeared. Go itself takes ten times less space. And there are also licensing restrictions: “this is the case.”

    What do we have left from the list? Ui might be a good option, but he is still in the mid-alpha stage. Fynealso looks good, but it seems not quite ready. It was somewhat embarrassing that, on the one hand, “Fyne is built entirely using vector graphics”, and, on the other, “EFL windows packages are like that. Well, I don’t like that in order to install EFL under Windows (the graphical library on which Fyne is based ), MSYS is needed .

    In short, with all due respect to the authors of the listed packages and to the products of their work, for myself I did not choose anything and with a clear conscience I proceeded to what I wanted to do - to write a new GUI framework - External .

    As I wrote in the previous article, Externalit does not implement GUI elements on its own, it uses a separate application for this, a separate process acting as a GUI server, this application is called GuiServer . External launches it, joins it via tcp / ip, sends commands / requests to create windows and widgets, manipulate them, etc., and receives messages from it.

    Here is the simplest program that creates a window with the traditional Hello, world text:

    package main
    import egui "github.com/alkresin/external"funcmain() {
       if egui.Init("") != 0 {
            return
        }
        pWindow := &egui.Widget{X: 100, Y: 100, W: 400, H: 140, Title: "My GUI app"}
        egui.InitMainWindow(pWindow)
        pWindow.AddWidget(&egui.Widget{Type: "label",
            X: 20, Y: 60, W: 160, H: 24, Title: "Hello, world!" })
        pWindow.Activate()
        egui.Exit()
    }

    The Init () function launches GuiServer and joins it. It can be passed a string parameter that determines, if necessary, the name of GuiServer and the path to it, ip address and port, logging level.

    InitMainWindow () creates the main application window with the specified parameters. The AddWidget () method adds a label widget.

    Activate () - displays a window on the screen and puts the program into standby mode.
    Both windows and widgets are defined in the Widget structure - I did not make separate structures for each object, since I did not find a convenient way to do this, given that in Gono inheritance. This structure includes fields common to most widgets, and map [string] string, where properties specific to a specific object are collected:

    type Widget struct {
          Parent   *Widget
          Type     string
          Name     string
          X        int
          [...]
          Font     *Font
          AProps   map[string]string
          aWidgets []*Widget
    }

    The methods of this structure include the already familiar AddWidget (), as well as SetText (), SetImage (), SetParam (), SetColor (), SetFont (), GetText (), Move (), Enable (), and others. I would like to mention SetCallBackProc () and SetCallBackFunc () - for setting event handlers.
    To list here all the functions, structures and methods would be inappropriate, for this there is, more precisely. there must be documentation. Let me just say a few, to give some general idea:

    Menu (), MenuContext (), EndMenu (), AddMenuItem (), AddMenuSeparator () - a set of functions for creating a menu, main or contextual.
    EvalProc (sCode string), EvalFunc (sCode string) transfer a fragment of the Harbor code (it can be multi-line) for execution on GuiServer - a kind of implementation of the embedded scripting language.
    OpenForm (sPath string) - creates a window based on the description from the xml file created by HwGui Designer.
    OpenReport (sPath string) - prints a report based on a description from an xml file created by HwGui Designer.
    MsgInfo (), ..., SelectFile (), SelectColor (), SelectFont () - a call to standard messageboxes and dialogs.
    InitPrinter () and the set of methods of the Printer: Say (), Box (), Line (), etc. structure provide for printing to the printer with the possibility of a preview.

    Here is a complete list of currently supported widgets:
    label, edit, button, check, radio, radiogr, group, combo, bitmap, line, panel (designed for placing other widgets on it), paneltop, panelbot, ownbtn (ownerdrawn button), splitter, updown, tree, progress, tab, browse (table, as many call it), cedit (edit with advanced features), monthcal.

    All of them are listed in the init () function extwidg.go along with additional properties. accessible to each of them - these properties are set via Widget.AProps. Many of the listed widgets have other properties, browse is especially rich in them; they can be set separately using the SetParam () method.

    External turned out to be small in volume, it is written in pure Go.It does not pull other packages other than a few standard ones. Cross-platform is provided by GuiServer , which can be compiled under Windows, Linux / Unix, Mac OS. A certain inconvenience is connected with the need to use this external module, which you need to either collect from source, or download it ready from my site and place in the directory specified in the PATH. By the way, it is small - only about one and a half megabytes for Windows and about three for Linux.

    How it looks, I will show on the example of a small application ETutor- Golang tutorial. This program presents a collection of Go code snippets in the form of a tree. The code can be edited, run. Nothing fancy, but pretty comfortable. The collection can be replenished, add new collections. Now there are collected (not yet fully) A Tour of Go, Go by Example and a few examples on the External itself . ETutor can also be used to, for example, streamline a set of any utilities on Go. So, screenshots.

    Windows 10:



    Debian, Gnome:



    And finally, the links:

    External on Github
    GuiServer on Github
    ETutor on Github The
    page about GuiServer on my website, where you can download ready-made binaries
    https://groups.google.com/d/forum/guiserver- Group to discuss all issues related to GuiServer and External
    Article on GuiServer on Habré

    Also popular now: