(sec-module-misc.gui)= # 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. (sec-gui--complainonce)= ## Function: _ComplainOnce [`_ComplainOnce(reason)`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L96) - **function description**: say once per process that the renderer's SystemContainer could not be used, and why (sec-gui-getrenderersystemcontainer)= ## Function: GetRendererSystemContainer [`GetRendererSystemContainer()`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L104) - **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). (sec-gui-makeprocessdpiaware)= ## Function: MakeProcessDpiAware [`MakeProcessDpiAware()`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L139) - **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 (sec-gui-tkfontsystemhint)= ## Function: TkFontSystemHint [`TkFontSystemHint(platform, fontSystem)`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L167) - **function description**: the hint for dialogs drawn without anti-aliasing, or '' if there is nothing to say - **input**: - `platform`: sys.platform - `fontSystem`: 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 (sec-gui-checktkfontsystem)= ## Function: CheckTkFontSystem [`CheckTkFontSystem(root)`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L184) - **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 (sec-gui-scaledfontsize)= ## Function: ScaledFontSize [`ScaledFontSize(size, fontScaling)`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L204) - **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 it - `fontScaling`: dialogs.fontScaling; 0 or less is 1 - **output**: the size in points, at least 6; 0 (the default of Tk) stays 0 (sec-gui-applydialogfontscaling)= ## Function: ApplyDialogFontScaling [`ApplyDialogFontScaling(root)`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L225) - **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 (sec-gui-gettkrootandnewwindow)= ## Function: GetTkRootAndNewWindow [`GetTkRootAndNewWindow()`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L248) - **function description**: get new or current root and new window app; return list of [tkRoot, tkWindow, tkRuns] (sec-gui-tkrootexists)= ## Function: TkRootExists [`TkRootExists()`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L267) - **function description**: this function returns True, if tkinter has already a root window (which is assumed to have already a mainloop running) (sec-gui-columnwidthfractions)= ## Function: ColumnWidthFractions [`ColumnWidthFractions(widths)`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L274) - **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. (sec-gui-dialogfontsize)= ## Function: DialogFontSize [`DialogFontSize(fontFactor)`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L297) - **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 (sec-gui-dialogrowmetrics)= ## Function: DialogRowMetrics [`DialogRowMetrics(root, fontFactor)`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L309) - **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 against - `fontFactor`: the multiplier DialogScaling returned - **output**: [rowHeight in pixels, columnScale relative to the unscaled font] (sec-gui-getexudyndisplayscaling)= ## Function: GetExudynDisplayScaling [`GetExudynDisplayScaling(root = None)`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L346) - **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 (sec-gui-dialogscaling)= ## Function: DialogScaling [`DialogScaling(root)`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L395) - **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] (sec-gui-splitstoredfromchanged)= ## Function: SplitStoredFromChanged [`SplitStoredFromChanged(changes, overriddenPaths, fileName)`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L428) - **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 to - `overriddenPaths`: the paths that `~/.exudyn/config.json` stores - `fileName`: 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. (sec-gui-renderstatecodelines)= ## Function: RenderStateCodeLines [`RenderStateCodeLines(renderState, systemContainerName = 'SC')`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L455) - **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 (sec-gui-editdictionarywithtypeinfo)= ## Function: EditDictionaryWithTypeInfo [`EditDictionaryWithTypeInfo(settingsStructure, exu = None, dictionaryName = 'edit')`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L1603) - **function description**: edit dictionaryData and return modified (new) dictionary - **input**: - `settingsStructure`: hierarchical settings structure, e.g., SC.visualizationSettings - `exu`: exudyn module - `dictionaryName`: name displayed in dialog - **output**: returns modified dictionary, which can be used, e.g., for SC.visualizationSettings.SetDictionary(...) (sec-gui-storedialogpositions)= ## Function: StoreDialogPositions [`StoreDialogPositions(settingsStructure = None)`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L1949) - **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 a `visualizationSettings`; 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 ask - **notes**: 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 because `python -m exudyn dialogs` has 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. (sec-gui-restorewindowgeometry)= ## Function: RestoreWindowGeometry [`RestoreWindowGeometry(tkWindow, name, width = None, height = None)`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L1982) - **function description**: Give a dialog the size and position it was left at, as far as that is safe. - **input**: - `tkWindow`: the window - `name`: the title of the dialog, which is what it is stored under - `width`: 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 needs - `height`: 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.storeDialogPositions` says (#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. (sec-gui-rememberwindowgeometry)= ## Function: RememberWindowGeometry [`RememberWindowGeometry(tkWindow, name, settingsStructure = None)`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L2036) - **function description**: Record where a dialog is while it lives, so that it can be stored when it closes. - **input**: - `tkWindow`: the window - `name`: the title of the dialog, which is what it is stored under - `settingsStructure`: the structure being edited, which is asked for `dialogs.storeDialogPositions` before the renderer's SystemContainer - **output**: a dictionary that `StoreWindowGeometry` reads; empty when nothing is to be stored - **notes**: 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 `` and the last value is the one that is stored. (sec-gui-storegeometrystring)= ## Function: StoreGeometryString [`StoreGeometryString(geometry, name)`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L2068) - **function description**: Store one 'WIDTHxHEIGHT+X+Y' under the name of a dialog. - **input**: - `geometry`: what tkWindow.geometry() reported - `name`: 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. (sec-gui-storewindowgeometry)= ## Function: StoreWindowGeometry [`StoreWindowGeometry(recorded, name)`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L2098) - **function description**: Store what `RememberWindowGeometry` recorded, after the dialog has closed. - **input**: - `recorded`: what `RememberWindowGeometry` returned - `name`: the title of the dialog (sec-gui-applydialogwindowsettings)= ## Function: ApplyDialogWindowSettings [`ApplyDialogWindowSettings(tkWindow, alwaysTopmost = None, alphaTransparency = None)`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L2108) - **function description**: Apply what visualizationSettings.dialogs says about a dialog window. - **input**: - `tkWindow`: the window to configure - `alwaysTopmost`: None takes dialogs.alwaysTopmost; True or False overrides it - `alphaTransparency`: None takes dialogs.alphaTransparency; a float overrides it - **output**: None (sec-gui-showhelpdialog)= ## Function: ShowHelpDialog [`ShowHelpDialog()`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L2138) - **function description**: The keyboard and mouse commands of the renderer, in a read-only window; opened with H in the render window. - **output**: None (sec-gui-modelscope)= ## Function: ModelScope [`ModelScope()`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L2192) - **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 where `mbs`, `SC` and 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 no `mbs` (#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 (sec-gui-showpythoncommanddialog)= ## Function: ShowPythonCommandDialog [`ShowPythonCommandDialog()`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L2208) - **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 (sec-gui-showvisualizationsettingsdialog)= ## Function: ShowVisualizationSettingsDialog [`ShowVisualizationSettingsDialog()`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L2296) - **function description**: The settings tree of the renderer; opened with V in the render window. - **output**: None (sec-gui-showrightmouseselectiondialog)= ## Function: ShowRightMouseSelectionDialog [`ShowRightMouseSelectionDialog()`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L2311) - **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 (sec-gui-askquitdialog)= ## Function: AskQuitDialog [`AskQuitDialog()`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L2326) - **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 (sec-module-misc.gui-class-tooltip)= ## 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. (sec-gui-tooltip-show)= ### Class function: Show [`Show(self, text, x, y)`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L506) - **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. (sec-gui-tooltip-cancel)= ### Class function: Cancel [`Cancel(self)`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L519) - **class function description**: forget a tooltip that was scheduled and has not appeared yet (sec-gui-tooltip-bind)= ### Class function: Bind [`Bind(self, widget, text)`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L528) - **class function description**: let a widget - a button, say - show this tooltip while the pointer rests on it (sec-gui-tooltip-place)= ### Class function: Place [`Place(self, text, x, y)`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/exudyn/misc/GUI.py#L533) - **class function description**: place the tooltip at the screen position (x, y), a little below the pointer