pyFormex scripting¶
While the pyFormex GUI provides some means for creating and transforming geometry, its main purpose and major strength is the powerful scripting language. It offers you unlimited possibilities to do whatever you want and to automize the creation of geometry up to an unmatched level.
Currently pyFormex provides two mechanisms to execute user applications: as a script, or as an app. The main menu bar of the GUI offers two menus reflecting this. While there are good reasons (of both historical and technical nature) for having these two mechanisms, the fist time user will probably not be interested in studying the precise details of the differences between the two models. It suffices to know that the script model is well suited for small, quick applications, e.g. often used to test out some ideas. As your application grows larger and larger, you will gain more from the app model. Both require that the source file(s) be correctly formatted Python scripts. By obeing some simple code structuring rules, it is even possible to write source files that can be executed under either of the two models. The pyFormex template script as well as the many examples coming with pyFormex show how to do it.
Scripts¶
A pyFormex script is a simple Python source script in a file (with ‘.py’
extension), which can be located anywhere on the filesystem. The script is
executed inside pyFormex with an exec
statement. pyFormex provides a
collection of global variables to these scripts: the globals of module
gui.draw
if the script is executed with the GUI, or those from the
module script
if pyformex was started with --nogui
. Also, the
global variable __name__
is set to either ‘draw’ or ‘script’, accordingly.
The automatic inclusion of globals has the advantage that the first time user
has a lot of functionality without having to know what he needs to import.
Every time the script is executed (e.g. using the start or rerun button),
the full source code is read, interpreted, and executed. This means that
changes made to the source file will become directly available. But it also
means that the source file has to be present. You can not run a script from
a compiled (.pyc
) file.
Apps¶
A pyFormex app is a Python module. It is usually provided as a Python
source file (.py
), but it can also be a compiled (.pyc
) file.
The app module is loaded with the import
statement. To allow this, the
file should be placed in a directory containing an ‘__init__.py’ file (marking
it as a Python package directory) and the directory should be on the pyFormex
search path for modules (which can be configured from the GUI App menu).
In order to be executable from the GUI, an app module should contain a function named ‘run’. When the application is started for the first time (in a session), the module is loaded and its ‘run’ function is executed. Each following execution will just execute the ‘run’ function again.
When loading a module from source code, it gets compiled to byte code
which is saved as a .pyc
file for faster loading next time. The
module is kept in memory until explicitely removed or reloaded
(another import
does not have any effect). During the loading of
a module, executable code placed in the outer scope of the module is
executed. Since this will only happen on first execution of the app,
the outer level should be seen as initialization code for your
application.
The ‘run’ function defines what the application needs to perform. It can be executed over and over by pushing the ‘PLAY’ button. Making changes to the app source code will not have any effect, because the module loaded in memory is not changed. If you need the module to be reloaded and the initialization code to be rerun use the ‘RERUN’ button: this will reload the module and execute ‘run’.
How to choose between script or app¶
Both scripts and apps have their pros and cons. We list some of them below
Script |
App |
|
|
? Read and compiled on every run |
? Read and compiled once per session or when explicitely requested, run many times unchanged |
Can only import functionality from a script structured as a module. |
Direct import from any other app. |
Attributes need to be searched and decoded from the soure text |
The module can have any attributes |
A script can not execute another |
One app can import and run another |
It is impossible to run multiple scripts in parallel. |
It might become possible to run multiple applications in parallel, e.g. in different viewports. |
Global variables of all scripts occupy single scope |
Each app has its own globals |
Scripts and plugins are two different things. |
Apps and plugins (menus or not) are both just normal Python modules. |
Exit requires special function |
Exit with the normal return statement |
Canvas settings are global to all scripts |
Canvas settings could be made local to applications |
Data persistence requires export to the pyFormex GUI dict PF and reload |
Data persistence between invokations is automatic (for module globals) |
In favor of script:
Script |
App |
Default set of globals provided |
Everything needs to be imported (can be limited to 1 extra line) |
Globals of previous scripts are accessible (may be unwanted) (IS THIS STILL TRUE?) |
Communication between scripts needs explicit exports (but is more sound) |
Users are used to it since longtime |
The difference is not large though. |
Can be located anywhere. |
Have to be under sys.path (can be configured and expanded). |
Can easily execute a small piece of Python code, not even in a file, eg ToolsMenu: Execute pyFormex command |
We may have to keep a basic script exec functionality next to the app framework |
Problems with apps¶
Apps with syntax errors can not be loaded nor run. Exceptions raised during application load are filtered out by default. Setting the configuration variable ‘raiseapploadexc’ to True will make such errors be shown.
Apps creating a permanent (non-blocking, modeless) dialog can currently not be rerun (reload and run). We could add such facility if we use a default attribute name, e.g. _dialog. Reloading would then close the dialog, and running would reopen it.
Environment for scripts/apps¶
When executing a script or an app, there are a lot of identifiers already defined. This is what we call the pyFormex core language. Historically, this included a huge number of definitions in the global namespace, but the intention is to cut this down in future, so using specific imports is highly recommended.
Common script/app template¶
The template below is a common structure that allows this source to be used both as a script or as an app, and with almost identical behavior.
1"""pyFormex Script/App Template 2 3Copyright 2022 Benedict Verhegghe (bverheg@gmail.com) 4Distributed under the GNU General Public License version 3 or later. 5 6Your source file should start with a docstring like this. 7The first line should hold a short description of the file's purpose. 8This is followed by a blank line and one or more lines with a more 9comprehensive explanation of the script's purpose. 10 11If you want to distribute your script/app, you should insert the 12name of the copyright holder and license near the start of this file. 13Make sure that you (the copyright holder) have the intention/right to 14distribute the software under the specified copyright license (GPL3 or later). 15 16This is a template file to show the general layout of a pyFormex 17script or app. 18 19A pyFormex script is just any simple Python source code file with 20extension '.py' and is fully read and execution at once. 21 22A pyFormex app can be a '.py' of '.pyc' file, and should define a function 23'run()' to be executed by pyFormex. Also, the app should import anything that 24it needs. 25 26This template is a common structure that allows the file to be used both as 27a script or as an app, with almost identical behavior. 28 29For more details, see the user guide under the `Scripting` section. 30""" 31 32# The pyFormex modeling language is defined by everything in 33# the gui.draw module (if you use the GUI). For execution without 34# the GUI, you should import from pyformex.script instead. 35from pyformex.gui.draw import * 36 37# Definitions 38def run(): 39 """Main function. 40 41 This is executed when you run the app. 42 """ 43 print("This is the pyFormex script/app template") 44 45# Code in the outer scope: 46# - for an app, this is only executed on loading (module initialization). 47# - for a script, this is executed on each run. 48 49print("This is the initialization code of the pyFormex template script/app") 50 51# The following is to make script and app behavior alike 52# When executing a script in GUI mode, the global variable __name__ is set 53# to '__draw__', thus the run method defined above will be executed. 54 55if __name__ == '__draw__': 56 print("Running as a script") 57 run() 58 59 60# End
The script/app source starts by preference with a docstring, consisting of a short first line, then a blank line and one or more lines explaining the intention and working of the script/app.
Convert a script to an app¶
The following steps will convert most pyFormex scripts into an equivalent app:
Put all the script code (except initial imports) into a function named ‘run’.
Replace all lines:
exit()
with:
return
Add a line on top to import everyhing from the gui.draw module:
from gui.draw import *
If you want the app to still be executable as a script, add the following at the bottom:
if __name__ == "draw"
run()
If your app/script should work both with or without the pyFormex GUI, use this structure:
import pyformex as pf
if pf.GUI:
from gui.draw import *
< DEFINITIONS FOR GUI VERSION >
else:
from gui.script import *
< DEFINITIONS FOR NONGUI VERSION >
< COMMON DEFINITIONS FOR BOTH CASES>
Of course, when your definitions become long it may be better to put them in separate files:
import pyformex as pf
if pg.GUI:
import myapp_gui
else:
import myapp_nongui