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.
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.
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 theMicrosoft Store
app -- typestore
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.
-
gopy
exe
mode builds a standalone executable calledpygi
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 asgi
,giv
,units
, etc. These are all installed duringmake install
into a single python module calledgi
. -
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 allgopy
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 aslice
ormap
orstruct
) "live" in the Go world, and the python side uses a uniquehandle
identifier (anint64
) 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-usedki.Props
class in Go, which is used for styling properties etc, must be used handled usingki.SetPropStr
andki.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 thegiv
View-based interfaces for native Python types, so that for example you can use apygiv.ClassView
to get an instant GUI editor of a Pythonclass
object, in the same way thatgiv.StructView
provides a GUI editor of Gostruct
s.