User settings that persist between runs
A setting that is changed on every start - the output directory, the multi-sampling of
the renderer, the size of the basis vectors - is stored once and taken by every run afterwards.
Exudyn reads one file for that, ~/.exudyn/config.json:
{
"version": 1,
"config": {"outputDirectory": "solution/"},
"visualizationSettings": {"openGL.multiSampling": 4, "nodes.basisSize": 0.5}
}
Nothing writes this file by itself. A script that behaves differently on another machine, because something was stored there, is the one thing a settings file must not cause, so storing is always asked for:
from exudyn.misc import overrideSettings
SC.visualizationSettings.openGL.multiSampling = 4
overrideSettings.Store(SC) #every run from now on starts with it
Store(SC) writes the settings that differ from the defaults - the same list
the settings dialog shows as changed - and Store(config=exudyn.config) does the same for
exudyn.config. overrideSettings.Clear() deletes the file.
The version is the format of the file, and it has to match: a file of another version is
ignored, with one note naming both. It is a plain integer that moves only when Exudyn has been
released and the meaning of something in this file has changed - not with the Exudyn version, which
moves on every resolved issue. Store your settings again to write a current file.
What happens when the file is there
import exudyn reads it once into exudyn.special.overrideSettings and prints
one note naming how many settings it took:
NOTE: 1 config settings and 2 visualizationSettings read from ~/.exudyn/config.json
A count that is 0 is left out. overrideSettings.Print() lists what came from the file, which
is the answer to why does this script behave differently here, overrideSettings.Applied() gives
the same as a list, for a script that wants to print it into its own output, and
EXUDYN_NO_USER_SETTINGS=1 runs a script as if there were no file.
A file that should stay quiet says so itself, beside its version:
{
"version": 1,
"suppressOverrideSettingsWarning": true,
"visualizationSettings": {"openGL.multiSampling": 4}
}
The key does not exist unless it is written, and without it the note is printed. It suppresses the note only: a setting that could not be applied is still reported, because that is a defect and not information.
What happens, in order:
the whole file is read into
exudyn.special.overrideSettings, once, byimport exudyn;what can be applied at once is applied at once, which is the
configsection;the rest stays in the dictionary, because the things it sets do not exist yet;
every
VisualizationSettingsstructure applies the stored settings when it is created - the one aSystemContainerbuilds, and one built byexu.VisualizationSettings(). A setting that names nothing is reported and changes nothing;a dialog reads its size and position from the
dialogssection when it opens, and the results monitor its own settings fromresultsMonitor;nothing writes the file unless it is asked to.
The file is read once, so a file you edit while a session is running - or one that another session stored - has no effect until you read it again. That is what makes a stored setting look as if it had not been stored in a console that keeps its kernel, such as Spyder:
from exudyn.misc import overrideSettings
overrideSettings.Reload() #read it again and apply what can be applied
overrideSettings.Print() #what came from it now
A reload does not undo: a setting that already reached exudyn.config, and a structure that
already exists, keep what they were given. A setting you removed from the file is seen by the
structures created after the reload; for everything else, start a new session.
A stored visualizationSetting is not a new default: exu.VisualizationSettings() carries it,
and the defaults the settings dialog compares against are still the defaults of Exudyn, so diff to
default shows a stored setting as a difference. That is the point - it is what the file changed.
What may be stored are plain values: a number, a flag, a string, or a list of numbers - and an
enum setting, contour.outputVariable, as the name of its value, "StressLocal", which is read back
by that name; interactive.highlightItemType is the state of a highlight and is not stored. A setting
that holds graphics data, a user function or a matrix container is refused with a message and
changes nothing - such a value cannot be carried honestly by a JSON file. A key that names no
setting, a section nobody reads, a file that is not valid JSON: each of them is reported and none of
them stops import exudyn.
Storing from the dialog
The settings dialog has a store settings button. It writes the visualizationSettings that
differ from the defaults, and the size and position of the dialog itself, and nothing else - not
exudyn.config, not the simulation settings - and it shows exactly what it is about to write before
it writes anything. It is the way to keep a look you have just made without switching
storeDialogPositions on.
diff to default stays a difference to the default, and a setting that the file already stores is listed with the others and then named again under a comment line that says so. Comparing against the defaults plus the file instead would hide exactly the settings the file is about.
Placing a dialog, and a window, from a script
A script can say where a dialog opens, which is the same mechanism the dialogs use for themselves:
from exudyn.misc import overrideSettings
#the visualization settings dialog, 1024x768 at (100, 80):
overrideSettings.StoreDialogGeometry('Visualization Settings', [1024, 768], [100, 80])
The name is the title of the dialog, and overrideSettings.DialogKey(name) is the key it is
stored under, so the same call places the solution viewer ('Solution Viewer') or any other dialog.
The size comes back always and the position only if the window would still be reachable on the
screen you have now - a monitor that is gone must not put a dialog where its title bar cannot be
grabbed. This writes ~/.exudyn/config.json, so it holds for every run afterwards.
The render window is not a dialog and is placed by its own settings, per view:
SC.visualizationSettings.view0.window.renderWindowSize = [1024, 768]
SC.visualizationSettings.view0.window.renderWindowPosition = [100, 80]
A negative coordinate - the default - means the window manager places it. The position is that of the OpenGL area rather than the title bar, so a small value hides part of the title bar and 0 hides it completely, which is a way to have a view without one.
The render window is the one window whose geometry lives in two places: these settings, and -
because they are ordinary settings - the visualizationSettings section of the file. The file is
applied when the settings structure is created and a script speaks afterwards, so what the script
sets wins, and SC.renderer.Start() says so once when the two differ:
Python WARNING: the render window geometry stored in the settings file differs from what this session
set, and what the session set is used:
view0.window.renderWindowPosition: the file says [100,80] and this session uses [500,400]
store the settings again to change the file, or remove them from it
It says nothing when they agree, which is the normal case for someone who stored the geometry and has
not touched it since. view*.window.storeRenderWindowGeometry writes where the window was back into
these settings when it closes, and SC.renderer.GetState()['currentWindowPosition'] is where it is
while it is open.
The plot windows of PlotSensor
A plot window is remembered by its sequence, the order PlotSensor made it in, because plot
windows have no title of their own: the first one of a run is stored as 'PlotSensor 1', the second
as 'PlotSensor 2', and PlotSensor(..., closeAll=True) starts that order over. They are stored in
the same dialogs section, with the same rules - the size comes back always, the position only if the
window would still be reachable - so the next run opens the plots where they were arranged.
Storing them is asked for, once the windows are where they should be:
from exudyn.plot import StorePlotWindowGeometry
#after arranging the plot windows on the screen:
StorePlotWindowGeometry() #returns how many windows it stored
This is the call to use when the plots are made after SC.renderer.Stop(), when the settings dialog
is gone; while it is open, its store positions button stores the plot windows together with the
other windows. PlotSensorDefaults().storeWindowPositions = True in addition stores each window when it closes, one window at a time, which asks nothing but
also keeps whatever a window happened to be when it was closed.
A plot window is only placed by a backend that has one: with matplotlib on Agg - which
EXUDYN_SUPPRESS_UI_WINDOW_OPEN selects - there is no window, nothing is placed and nothing is
stored.
For this run only, a script can write into exudyn.special.overrideSettings instead of the file:
exudyn.special.overrideSettings['dialogs'] = {
'visualizationsettings': {'size': [1024, 768], 'position': [100, 80]}}
That is read by the next dialog that opens and the file is not touched. It is not the recommended way: nothing checks what is put there, and a value of the wrong shape is simply not used - the functions above are what say what they mean, and what a later Exudyn will keep working.
Remembering a dialog, its columns and its font
visualizationSettings.dialogs.storeDialogPositions = True makes a settings dialog remember
where it was left, under "dialogs", one entry per dialog. The size comes back always; the
position only when the window would still be reachable. A monitor that is unplugged, a laptop
undocked, a screen resolution that changed: each of them would otherwise put the dialog where nobody
can reach its title bar, and a dialog that cannot be closed is a stuck session. When the stored
position is not usable, the dialog opens where it would have opened anyway, at its remembered size.
The three fixed columns of a settings dialog take a share of its width, each a fraction in
visualizationSettings.dialogs - columnWidthName, columnWidthValue,
columnWidthType - and the description column takes what they leave; if the three together
would leave the description less than a tenth of the dialog, all three are scaled down to leave it
that much. Ctrl and the mouse wheel change the font size of an open dialog, about 10% per notch;
the wheel alone still scrolls.
The environment variables that change what Exudyn does before a script says anything are listed in
Environment variables; EXUDYN_NO_USER_SETTINGS and EXUDYN_CONFIG_FILE are the two for this file.
The functions that read and write the file are in exudyn.misc.overrideSettings.