Module: misc.GUI
Helper functions and classes for graphical interaction with Exudyn
Author: Johannes Gerstmayr
Date: 2020-01-25
Notes: This is an internal library, which is only used inside Exudyn for modifying settings.
Function: _ComplainOnce
function description: say once per process that the renderer’s SystemContainer could not be used, and why
Function: GetRendererSystemContainer
function description: The SystemContainer that is attached to the render engine, or None.
output: the container, or None when no renderer is running, when the entry names a container whose C++ object is gone, or when it names something else entirely
notes: None is the normal answer without a renderer and is not reported. Anything ELSE that goes wrong here says so once per process: a dialog that finds no container does nothing, and nothing being printed is how “the V key opens no dialog” became a reproduction rather than a message (#2691, maintainer 2026-09-26).
Function: MakeProcessDpiAware
function description: tell Windows that this process draws at the real resolution of the display Without it, Windows renders the window at 96 dpi and stretches the bitmap, which is why a dialog opened from a shell looked soft while the same dialog opened from the render window was sharp: GLFW makes the process DPI aware when it creates the render window, and a dialog that comes after it inherits that. From “python -m exudyn dialogs” there is no GLFW (#2634). It must be called BEFORE the first window is created; afterwards Windows refuses, which is not an error here - something else has already set it, which is what we wanted.
output: True if this process is DPI aware afterwards
Function: TkFontSystemHint
TkFontSystemHint(platform, fontSystem)
function description: the hint for dialogs drawn without anti-aliasing, or ‘’ if there is nothing to say
input:
platform: sys.platformfontSystem: what Tk reports as its font system, ‘xft’ or ‘x11’ on Linux
output: the text to print, ‘’ on Windows and macOS and for a Tk with Xft
Function: CheckTkFontSystem
function description: say once, on Linux, that the Tk of this Python has no Xft, which makes the dialog fonts jagged and coarse (#2890)
input:
root: the tkinter root that was just created
Function: ScaledFontSize
ScaledFontSize(size, fontScaling)
function description: a Tk font size in points, times dialogs.fontScaling A negative size is pixels - the named fonts of Tk on Linux are, e.g. -12 - and pixels do not follow the scaling of Tk, which made the buttons and edit fields stay small while the text of the tree grew (#2898). It is converted to points at 96 dpi first, so that every named font follows the display scaling once, through the scaling of Tk.
input:
size: the size as Tk gives itfontScaling: dialogs.fontScaling; 0 or less is 1
output: the size in points, at least 6; 0 (the default of Tk) stays 0
Function: ApplyDialogFontScaling
function description: set the named fonts of Tk (TkDefaultFont, TkTextFont, TkFixedFont, …) in points, times dialogs.fontScaling, so that every widget of a dialog - text, buttons, edit fields, menus - takes the same font and follows the display scaling through the scaling of Tk (#2897, #2898) It is called before the widgets of a dialog are created, so nothing is rescaled while a window opens.
input:
root: the tkinter root, which owns the named fonts
Function: GetTkRootAndNewWindow
function description: get new or current root and new window app; return list of [tkRoot, tkWindow, tkRuns]
Function: TkRootExists
function description: this function returns True, if tkinter has already a root window (which is assumed to have already a mainloop running)
Function: ColumnWidthFractions
function description: the three column fractions, usable whatever was configured
input:
widths:[name, value, type], each a fraction of the dialog width
output: the same three, each at least 0.05, and scaled down together if they would leave the description column less than 10% of the dialog
notes: A settings dialog whose description column is a few pixels wide cannot be read, and the settings that produce it are three independent numbers a user may set in any order. So the rule is applied here rather than refused at the setting: what a user asks for, as far as it still leaves a dialog.
Function: DialogFontSize
function description: the point size the tree, its headings and its tags use
input:
fontFactor: the multiplier DialogScaling returned
output: the size in points, never below 6
Function: DialogRowMetrics
DialogRowMetrics(root, fontFactor)
function description: how tall a row must be and how much wider the columns must be, MEASURED Both used to be computed from systemScaling, which is not what decides how large a glyph comes out: the point-to-pixel conversion follows the tk scaling of the display, so at dialogs.fontScaling=1 the rows were 13 pixels tall for a font with a linespace of 16 to 18 and the text was clipped, while the column factor max(1,int(round(systemScaling))) stayed at 1 for every value below 1.5 (#2631). The font is asked instead, which is right at any scaling and on any platform.
input:
root: the tkinter root, which the fonts are measured againstfontFactor: the multiplier DialogScaling returned
output: [rowHeight in pixels, columnScale relative to the unscaled font]
Function: GetExudynDisplayScaling
GetExudynDisplayScaling(root = None)
function description: the display scaling the dialogs size themselves by The renderer knows it and reports it in its state. Without a renderer this used to return 1, so a dialog opened from the command line came out at a different size than the same dialog opened with V in the render window (#2634); tkinter is asked instead, which knows it once MakeProcessDpiAware has been called.
input:
root: a tkinter root to ask when there is no renderer; without one the answer is 1
output: the scaling, 1 if nothing knows better
Function: DialogScaling
function description: How tall a row of a dialog is and how large its font is, as [systemScaling, fontFactor]. dialogs.fontScaling is 0 by default, and 0 means what every platform did before the setting existed: a fixed factor on MacOS, the system display scaling on Windows and Linux. A value > 0 sets the font on EVERY platform, which is what makes the dialogs readable on a Linux desktop - off MacOS the font factor used to be forced to 1 and nothing could change it (#2602).
input:
root: the tkinter root window, which knows the display scaling
output: [systemScaling, fontFactor]
Function: SplitStoredFromChanged
SplitStoredFromChanged(changes, overriddenPaths, fileName)
function description: The changed settings, with the ones the override file stores named separately.
input:
changes: [(path, line)] as SettingsCodeLines returns them - every setting that differs from the DEFAULT, which is what the difference is tooverriddenPaths: the paths that~/.exudyn/config.jsonstoresfileName: the name of that file, for the comment line
output: (lines, storedCount): the same pairs, the stored ones last and behind a comment line, and how many of them there are
notes: The maintainer decided this on 2026-09-26: the difference is to the REAL default, and what the file already covers is named separately with a comment between them, so that a user can see what they would be copying and decide. Comparing against default-plus-override instead would hide exactly the settings that file is about.
Function: RenderStateCodeLines
RenderStateCodeLines(renderState, systemContainerName = 'SC')
function description: The lines that give a script the current model view of the render window, as CTRL+F3 prints them (#2862).
input:
renderState: the dictionary of SC.renderer.GetState()systemContainerName: the name of the SystemContainer in the script
output: [(key, line)]: SC.renderer.Start() and SC.renderer.SetModelView with zoom, rotation vector and center point
Function: EditDictionaryWithTypeInfo
EditDictionaryWithTypeInfo(settingsStructure, exu = None, dictionaryName = 'edit')
function description: edit dictionaryData and return modified (new) dictionary
input:
settingsStructure: hierarchical settings structure, e.g., SC.visualizationSettingsexu: exudyn moduledictionaryName: name displayed in dialog
output: returns modified dictionary, which can be used, e.g., for SC.visualizationSettings.SetDictionary(…)
Function: StoreDialogPositions
StoreDialogPositions(settingsStructure = None)
function description: True if a dialog should store where it was left, when it closes.
input:
settingsStructure: the structure the dialog is editing, if it is avisualizationSettings; it is asked FIRST, because it is the one the user is looking at
output: the value of
visualizationSettings.dialogs.storeDialogPositions, or False when there is nothing to asknotes: This decides whether a dialog stores ITSELF on closing, and nothing else: a geometry that is already stored - by this flag, by the store button or by a script - is used whenever a dialog opens, see
RestoreWindowGeometry(#2686). The structure is asked before the renderer’s SystemContainer becausepython -m exudyn dialogshas no container at all, and a script that has not started the renderer has none that can be found - so the flag used to be False there however it was set, and such a dialog could never store itself.
Function: RestoreWindowGeometry
RestoreWindowGeometry(tkWindow, name, width = None, height = None)
function description: Give a dialog the size and position it was left at, as far as that is safe.
input:
tkWindow: the windowname: the title of the dialog, which is what it is stored underwidth: the width it would otherwise get, in pixels; None leaves the size to the layout when nothing is stored, which is what a dialog whose size is computed from its widgets needsheight: the height it would otherwise get, or None
output: None
notes: The size is restored always and the position only when the window would still be reachable on the current screen. A monitor that is gone, a resolution that changed, a laptop that was undocked: each of them would otherwise put the dialog where nobody can close it. A GEOMETRY THAT IS STORED IS USED, whatever
dialogs.storeDialogPositionssays (#2686). That flag decides whether a dialog stores ITSELF when it closes; this function only reads what is there, and it is there because the flag was on, or because the store button of the dialog wrote it, or because a script did. Asking the flag here is what made the store button (#2685) write something that nothing read back.
Function: RememberWindowGeometry
RememberWindowGeometry(tkWindow, name, settingsStructure = None)
function description: Record where a dialog is while it lives, so that it can be stored when it closes.
input:
tkWindow: the windowname: the title of the dialog, which is what it is stored undersettingsStructure: the structure being edited, which is asked fordialogs.storeDialogPositionsbefore the renderer’s SystemContainer
output: a dictionary that
StoreWindowGeometryreads; empty when nothing is to be storednotes: The geometry cannot be read after the window is destroyed, and a dialog is left in three ways - the close button, Escape, and the window manager - so it is recorded on every
<Configure>and the last value is the one that is stored.
Function: StoreGeometryString
StoreGeometryString(geometry, name)
function description: Store one ‘WIDTHxHEIGHT+X+Y’ under the name of a dialog.
input:
geometry: what tkWindow.geometry() reportedname: the title of the dialog
output: True if it was stored
notes: This does NOT ask whether storeDialogPositions is on: the flag decides whether a dialog remembers itself on closing, and the store button of the settings dialog stores on request (#2685). RememberWindowGeometry is where the flag is read.
Function: StoreWindowGeometry
StoreWindowGeometry(recorded, name)
function description: Store what
RememberWindowGeometryrecorded, after the dialog has closed.input:
recorded: whatRememberWindowGeometryreturnedname: the title of the dialog
Function: ApplyDialogWindowSettings
ApplyDialogWindowSettings(tkWindow, alwaysTopmost = None, alphaTransparency = None)
function description: Apply what visualizationSettings.dialogs says about a dialog window.
input:
tkWindow: the window to configurealwaysTopmost: None takes dialogs.alwaysTopmost; True or False overrides italphaTransparency: None takes dialogs.alphaTransparency; a float overrides it
output: None
Function: ShowHelpDialog
function description: The keyboard and mouse commands of the renderer, in a read-only window; opened with H in the render window.
output: None
Function: ModelScope
function description: the namespace a command of the command window runs in: the one the MODEL lives in
__main__is the script the user started, or the console they are typing in, so this is wherembs,SCand everything else the model defined are. The command window used to be a Python string that the C++ executed in exactly this namespace; as a function of this module it would otherwise see the module’s own globals, where there is nombs(#2654).output: the dictionary of
__main__, which is written to as well as read: an assignment in the command window has to survive the command
Function: ShowPythonCommandDialog
function description: A window that executes a Python command in the global scope of the running model; opened with X in the render window. CTRL+RETURN runs what is in the text area.
output: None
Function: ShowVisualizationSettingsDialog
ShowVisualizationSettingsDialog()
function description: The settings tree of the renderer; opened with V in the render window.
output: None
Function: ShowRightMouseSelectionDialog
ShowRightMouseSelectionDialog()
function description: The properties of the item the right mouse button selected, read-only; the renderer has put them into exudyn.sys[‘currentRendererSelectionDict’] before calling this.
output: None
Function: AskQuitDialog
function description: Ask whether a long running simulation really shall be stopped; the answer goes back to the renderer in exudyn.sys[‘quitResponse’], as 2 (do not quit) or 3 (quit).
output: None
CLASS Tooltip (in module misc.GUI)
class description: The small yellow window that shows the description of the row under the mouse. tkinter has none, and the description used to be behind the key ‘h’ and a modal message box - which is not where a reader looks for it (#2601). It is a borderless Toplevel that is created when it is first needed and hidden afterwards, so a dialog that is never hovered never builds one. It is topmost, and that is not decoration: the dialog itself is topmost - it has to be, it blocks the render window - and on Windows a topmost window is always above one that is not, so a tooltip without the flag opens BEHIND the dialog and looks like a tooltip that never comes (#2639). The same mechanism hid the window of the changes in #2621. Turning dialogs.alwaysTopmost off made the tooltips work, which is what named the cause.
Class function: Show
class function description: show the tooltip after the delay, at the screen position (x, y) The delay is the maintainer’s (#2614): a description that appears the moment the pointer crosses a row is annoying, and with the find (#2607) nobody has to sweep the tree to find a setting any more.
Class function: Cancel
class function description: forget a tooltip that was scheduled and has not appeared yet
Class function: Bind
class function description: let a widget - a button, say - show this tooltip while the pointer rests on it
Class function: Place
class function description: place the tooltip at the screen position (x, y), a little below the pointer