Skip to content

Latest commit

 

History

History
98 lines (63 loc) · 8.35 KB

README.md

File metadata and controls

98 lines (63 loc) · 8.35 KB

Python wrapping of GoGi

You can now run most of GoGi via Python, using a newly-updated version of the gopy tool that automatically creates Python bindings for Go packages.

Go incorporates many features found in Python, and provides a really natural "backend" language for computationally-intensive functionality such as a GUI. Python provides a nice interactive "scripting" level interface for dynamically building GUI's, and can bring Go code to a much wider audience. Thus, this represents an ideal combination of the two languages. And you can build the entire stack, from the raw Go code in GoGi to the Python bindings (which unfortunately are a bit slow because they rely on a C compiler..), in a tiny fraction of the time it takes to build something like Qt and the PySide or PyQt bindings.

Installation

Python version 3 (3.6-3.9 have been well tested) is recommended, and the instructions assume that version (you can probably get version 2 to work but it has not been tested). Also pip must be installed, as is typical. This assumes you have already installed GoGi per the Wiki Install instructions, including installing Go itself, and adding ~/go/bin to your PATH. be double-sure that goki/examples/widgets runs properly per wiki install before proceeding -- if that doesn't work, nothing else will.

On linux, you must ensure that the linker ld will look in the current directory for library files -- add this to your .bashrc file (and source that file after editing, or enter command locally):

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.

This assumes that you are using go modules, as discussed in the wiki install page, and that you are in the gi directory where you installed gi (e.g., git clone https://goki.dev/goki/gi and then cd gi)

NOTE: only a modules version of the build is supported, but it has some weirdness about updating the go.mod and go.sum files across the gopy and rest of the build. Pay attention to the output of the make command and if anything is downloaded during the gopy part of the build, it is a good idea to interrupt the go build process (hit Ctrl-C) and start over again, until there are no downloading messages -- then everything should be included properly.

Do NOT do a go mod tidy (which is normally the right thing to do to fix up your go.mod file and download packages) because the context in which that needs to be done involves the files generated by gopy (in the gi subdir -- safe to do there after gopy runs), not what is present in the python directory where you type make.

$ cd python  # should be in gi/python now -- i.e., the dir where this README.md is..
$ make  # if you get an error about not finding gopy, make sure ~/go/bin is on your path
$ make install  # may need to do: sudo make install -- installs into /usr/local/bin and python site-packages
$ cd ../examples/widgets
$ ./widgets.py        # start using #! comment magic code

Alternatively, you can run things more manually in the widgets directory:

$ pygi   # this was installed during make install into /usr/local/bin
$ import widgets  # this loads and runs widgets.py -- view that and compare with widgets.go
$ pygi -i widgets.py  # another way to start it

If you get something like this error:

dyld: Library not loaded: @rpath/libpython3.6m.dylib

then you need to make sure that this lib is on your LD_LIBRARY_PATH -- on mac you can do otool -L /usr/local/bin/pygi and on linux it is ldd /usr/local/bin/pygi -- that should show you where it is trying to find that library.

If pkg-config or some other misc command is not found, you can use brew install to install it using homebrew on mac, or your package manager on linux.

Windows

As usual, Microsoft Windows is different -- here's some tips for configuring the client to work on windows.

  • First, strongly recommend using the PowerShell -- search for that in the Microsoft Store app -- type store in the windows taskbar search prompt -- this provides a much better command-line interface than the standard Command Prompt.

  • If you haven't already, install git from here: https://git-scm.com/download/win

  • Install gnu make from here: https://www.gnu.org/software/make/

  • Install Python from the main Python distribution: https://www.python.org/downloads/windows/ -- do not under any circumstances install from the Microsoft Store app! while that is very convenient, it creates symbolic links to access the python executables, which is incompatible with go exec.Command, preventing use of gopy.

    The standard python install does not create a python3.exe which grunt looks for -- follow instructions here: https://stackoverflow.com/questions/39910730/python3-is-not-recognized-as-an-internal-or-external-command-operable-program/41492852 (just make a copy of python.exe to python3.exe in the relevant installed location).

  • Use make install-win to install the executable -- it adds the .exe which is otherwise not added, and also installs the exe to ~/go/bin which is typically on the path, while /usr/local/bin is not standard for windows -- it is copied there anyway.

How it works

  • gopy exe mode builds a standalone executable called pygi that combines the python interpreter and shell, with the GoGi Go libraries. This is needed because the GUI event loop must run on the main thread, which otherwise is taken by the python interpreter if you try to load GoGi as a library into the standard python executable (gopy can also be loaded as a library for other cases where the thread conflict is not a problem).

  • The entire gi codebase is available via stub functions in a _gi module. There are various .py python wrappers into that _gi module corresponding to each of the different packages in GoGi, such as gi, giv, units, etc. These are all installed during make install into a single python module called gi.

  • As you can see in the widgets.py file, you load the individual packages as follows:

from gi import go, gi, giv, units, ki, gimain
  • The go package is common to all gopy modules and is the home of all the standard go types and any other misc types outside the scope of the processed packages, that are referred to by those packages. For example, go.Slice_int is a standard Go []int slice. go.GoClass is the base class of all class wrappers generated by gopy, so it can be used to determine if something is a go-based class or not:
if isinstance(ojb, go.GoClass):
  • All non-basic types (e.g., anything that is not an int float string, such as a slice or map or struct) "live" in the Go world, and the python side uses a unique handle identifier (an int64) to refer to those Go objects. Most of the time the handles are handled automatically by the python wrapper, but sometimes, you'll see code that initializes a new object using these handles, as in a callback function:
def strdlgcb(recv, send, sig, data):
    dlg = gi.Dialog(handle=send) # send is a raw int64 handle -- use it to initialize
    # a python wrapper class of type gi.Dialog -- note: you must get these types
    # right and crashes are likely if you don't!
  • Unfortunately, Python does not allow callback functions to be class methods, if the thing calling the callback (i.g., GoGi) is running on a different thread, which it is. Thus, you can only use separate standalone functions for callbacks. All GoGi callbacks have that same signature of recv, send, sig, and data, and data is automatically converted to a string type because the native Go interface{} type is not really managable within Python.

  • Also due to the issues with the interface{} type, the widely-used ki.Props class in Go, which is used for styling properties etc, must be used handled using ki.SetPropStr and ki.SetSubProps methods, etc, which set specific types of properties (strings, sub-Props properties). In general, most style properties can be set using a string representation, so just use that wherever possible.

  • A Python-based module called pygiv is under development, which will provide the giv View-based interfaces for native Python types, so that for example you can use a pygiv.ClassView to get an instant GUI editor of a Python class object, in the same way that giv.StructView provides a GUI editor of Go structs.