1 | # Python GUI for Framsticks library and server
|
---|
2 |
|
---|
3 | ## Table of contents
|
---|
4 |
|
---|
5 | - [About](#about)
|
---|
6 | - [Running](#running)
|
---|
7 | - [Sources and directories](#sources-and-directories)
|
---|
8 | - [Threading](#threading)
|
---|
9 | - [Development](#development)
|
---|
10 |
|
---|
11 |
|
---|
12 |
|
---|
13 | ## About
|
---|
14 |
|
---|
15 | This GUI offers basic features when connecting to a native Framsticks library (```dll/so/dylib```) or the [Framsticks server](https://www.framsticks.com/common/server.html). While there exist native GUIs for Framsticks which are much faster and offer more features, the advantage of this GUI is that it is written entirely in Python (thus being portable), it is entirely open-source, and can be freely modified or serve as an implementation example. This GUI is also completely separate from the Framsticks simulator (either the library or the server).
|
---|
16 |
|
---|
17 |
|
---|
18 |
|
---|
19 |
|
---|
20 | ## Running
|
---|
21 |
|
---|
22 | Run the ```gui.py``` script.
|
---|
23 |
|
---|
24 |
|
---|
25 |
|
---|
26 |
|
---|
27 | ## Sources and directories
|
---|
28 |
|
---|
29 | ```text
|
---|
30 | framspy/
|
---|
31 | ├── frams.py
|
---|
32 | ├── gui.py
|
---|
33 | └── gui/
|
---|
34 | ├── framsutils/
|
---|
35 | ├── res/
|
---|
36 | │ ├── icons/
|
---|
37 | │ ├── img/
|
---|
38 | │ └── obj/
|
---|
39 | ├── tests/
|
---|
40 | ├── visual/
|
---|
41 | │ └── shaders/
|
---|
42 | └── widgets/
|
---|
43 | ```
|
---|
44 |
|
---|
45 | - ```framsutils``` contains all classes used for low-level data manipulation.
|
---|
46 | - ```res``` is a resource directory.
|
---|
47 | - ```tests``` contains basic test classes.
|
---|
48 | - ```visual``` contains all OpenGL-related classes and files (shaders).
|
---|
49 | - ```widgets``` contains all tkinter widgets.
|
---|
50 |
|
---|
51 |
|
---|
52 |
|
---|
53 |
|
---|
54 | ## Threading
|
---|
55 |
|
---|
56 | The GUI runs the ```Tkinter``` main loop in the main thread.
|
---|
57 |
|
---|
58 | ```glFrame``` runs an additional thread to periodically and asynchronously obtain creatures data to render without freezing the render window. This is especially required for the socket connection, because requesting data is time-consuming relatively to rendering.
|
---|
59 |
|
---|
60 | ```FramsSocket``` runs the communication module with ```asyncio``` in another thread to provide fast and concurrent network communication.
|
---|
61 |
|
---|
62 | The communication module uses another thread to detect asynchronous events from server and to call an adequate callback.
|
---|
63 |
|
---|
64 |
|
---|
65 |
|
---|
66 |
|
---|
67 |
|
---|
68 | ## Development
|
---|
69 |
|
---|
70 | ### Communication
|
---|
71 |
|
---|
72 | If you want to add another feature to socket communication, send feature negotiation request in ```CommWrapper```'s *start* method in ```comm.py```.
|
---|
73 |
|
---|
74 | There is no generic system for registering for new events in the socket communication. If you want to register for a new event, add callback in ```EventConsumer```'s *run* method in ```comm.py``` and register for an event in ```FramsSocket```'s *initConnection* method in ```FramsSocket.py```.
|
---|
75 |
|
---|
76 | ### UI
|
---|
77 |
|
---|
78 | All property features for input widgets are handled in ```framsProperty.py``` in the *propertyToTkinter* function.
|
---|
79 |
|
---|
80 | All widget's definitions for the main window are added in the *\_\_init\_\_* method of the ```MainPage``` class in ```mainPage.py```. This method is divided into sections:
|
---|
81 | - OPENGL FRAME
|
---|
82 | - SIDE FRAME
|
---|
83 | - CONTROL PANEL
|
---|
84 | - TREEVIEW
|
---|
85 | - STATUS BAR
|
---|
86 | - MENU BAR
|
---|
87 | - WINDOW
|
---|
88 | - ORGANIZE WINDOWS POSITIONS for all displayed windows
|
---|
89 |
|
---|
90 | If you need to ask for more fields in ```ListGenePoolWindow``` or ```ListPopulationsWindow```, add their ids to *self.fields* list. If you want to show them as columns, make sure to add them in the right order at the begining of the list, because only *len(self.headers)* first fields with *self.headers* headers are shown. Also fill *self.headers_width* with the correct percentage value of the initial window width, for example:
|
---|
91 | ```python
|
---|
92 | fields = ["name", "genotype", "numjoints", "index", "uid"]
|
---|
93 | headers = ["Name", "Genotype"]
|
---|
94 | headers_width = [0.5, 0.5]
|
---|
95 | ```
|
---|
96 | The above example asks for *name*, *genotype*, *numjoints*, *index*, *uid*, but only shows *name* in the *Name* column and *genotype* in the *Genotype* column with 50% and 50% widths for each column. The remaining fields can be used during the preparation of the row.
|
---|
97 |
|
---|
98 | ### Contributing
|
---|
99 |
|
---|
100 | There are a number of "TODO" items (features, bugs) for this Python GUI described [here](https://docs.google.com/document/d/1KeY6RceeeiPDkt_Z6S2GihpcA4YsfujNfk3AErvXP3Q/edit?usp=sharing). Contact ```support@framsticks.com``` to learn more about the development.
|
---|
101 |
|
---|