From d48b37bee54f4bfc376c6c3f59b5c67793e128fb Mon Sep 17 00:00:00 2001 From: MohamedASLI Date: Wed, 3 Sep 2025 10:56:14 +0200 Subject: [PATCH 1/8] add config option to close other outputs in settings --- ...ttingsWindow.ResourceDictionary.en_us.xaml | 6 +++ ...ttingsWindow.ResourceDictionary.fr_fr.xaml | 6 +++ .../SettingsWindow.ResourceDictionary.ru.xaml | 6 +++ .../Settings.smartbutton/SettingsWindow.xaml | 18 +++++++ .../Settings.smartbutton/script.py | 13 +++++ pyrevitlib/pyrevit/script.py | 47 ++++++++++++------- pyrevitlib/pyrevit/userconfig.py | 30 ++++++++++++ 7 files changed, 110 insertions(+), 16 deletions(-) diff --git a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.en_us.xaml b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.en_us.xaml index 295cf6646..6ca7ae96d 100644 --- a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.en_us.xaml +++ b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.en_us.xaml @@ -57,6 +57,12 @@ Misc options for pyRevit development Load Beta Tools (Scripts with __beta__ = True, Reload is required) + Close other open consoles + + If enabled, pyRevit will close other output consoles when a new script is run. + This helps to reduce the number of open output windows. + + Caching Reset Caching to default 200 diff --git a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.fr_fr.xaml b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.fr_fr.xaml index 338cf6e85..294887d7c 100644 --- a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.fr_fr.xaml +++ b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.fr_fr.xaml @@ -57,6 +57,12 @@ Options diverses pour le développement de pyRevit Charger les outils bêta (Scripts avec __beta__ = True, le rechargement est requis) + Fermer les consoles ouvertes + + Si activé, pyRevit fermera les autres consoles de sortie lorsqu'un nouveau script est exécuté. + Cela aide à réduire le nombre de fenêtres de sortie ouvertes simultanément. + + Mise en cache Reset la mise en cache par défaut 200 diff --git a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.ru.xaml b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.ru.xaml index 6080cde86..152f6eef4 100644 --- a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.ru.xaml +++ b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.ru.xaml @@ -58,6 +58,12 @@ Разные настройки разработки pyRevit Загружать бета-инструменты (Скрипты с __beta__ = True, требуется перезапуск) + Закрывать другие открытые консоли + + Если включено, pyRevit будет закрывать другие консоли вывода при запуске нового скрипта. + Это помогает уменьшить количество одновременно открытых окон вывода. + + Кеширование Сбросить настройки кеширования 220 diff --git a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.xaml b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.xaml index d208f7d4c..750a708bd 100644 --- a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.xaml +++ b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.xaml @@ -205,6 +205,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/script.py b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/script.py index 553d553ea..f3ad9a38b 100644 --- a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/script.py +++ b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/script.py @@ -156,6 +156,13 @@ def _setup_core_options(self): self.minhostdrivefreespace_tb.Text = str(user_config.min_host_drivefreespace) self.loadbetatools_cb.IsChecked = user_config.load_beta + self.minimize_consoles_cb.IsChecked = user_config.output_close_others + if user_config.output_close_mode == 'current_command': + self.closewindows_current_rb.IsChecked = True + self.closewindows_orphaned_rb.IsChecked = False + else: # 'orphaned' + self.closewindows_current_rb.IsChecked = False + self.closewindows_orphaned_rb.IsChecked = True def _setup_engines(self): """Sets up the list of available engines.""" @@ -846,6 +853,12 @@ def _save_core_options(self): user_config.load_beta = self.loadbetatools_cb.IsChecked + user_config.output_close_others = self.minimize_consoles_cb.IsChecked + if self.closewindows_current_rb.IsChecked: + user_config.output_close_mode = 'current_command' + else: + user_config.output_close_mode = 'orphaned' + def _save_engines(self): # set active cpython engine engine_cfg = self.cpythonEngines.SelectedItem diff --git a/pyrevitlib/pyrevit/script.py b/pyrevitlib/pyrevit/script.py index fe300d922..0d5892060 100644 --- a/pyrevitlib/pyrevit/script.py +++ b/pyrevitlib/pyrevit/script.py @@ -154,7 +154,22 @@ def get_output(): Returns: (pyrevit.output.PyRevitOutputWindow): Output wrapper object """ - return output.get_output() + from pyrevit.userconfig import user_config + + # Get the output window using the output module's get_output() function + output_window = output.get_output() + + # Apply user configuration for output window management + if user_config.output_close_others: + try: + if user_config.output_close_mode == 'current_command': + output_window.close_others() + else: + output_window.close_others(True) + except: + pass # Silently fail if not supported + + return output_window def get_config(section=None): @@ -264,7 +279,7 @@ def get_data_file(file_id, file_ext, add_cmd_name=False): script.get_data_file('mydata', 'data', add_cmd_name=True) ``` '/pyRevit_2018_Command Name_mydata.data' - + Data files are not cleaned up at pyRevit startup. Script should manage cleaning up these files. @@ -547,7 +562,7 @@ def load_ui(ui_instance, ui_file='ui.xaml', handle_esc=True, set_owner=True): """Load xaml file into given window instance. If window instance defines a method named `setup` it - will be called after loading + will be called after loading Args: ui_instance (forms.WPFWindow): ui form instance @@ -679,29 +694,29 @@ def store_data(slot_name, data, this_project=True): ```python from pyrevit import revit from pyrevit import script - - + + class CustomData(object): def __init__(self, count, element_ids): self._count = count # serializes the Revit native objects self._elmnt_ids = [revit.serialize(x) for x in element_ids] - + @property def count(self): return self._count - + @property def element_ids(self): # de-serializes the Revit native objects return [x.deserialize() for x in self._elmnt_ids] - - + + mydata = CustomData( count=3, element_ids=[, , ] ) - + script.store_data("Selected Elements", mydata) ``` @@ -740,24 +755,24 @@ def load_data(slot_name, this_project=True): ```python from pyrevit import revit from pyrevit import script - - + + class CustomData(object): def __init__(self, count, element_ids): self._count = count # serializes the Revit native objects self._elmnt_ids = [revit.serialize(x) for x in element_ids] - + @property def count(self): return self._count - + @property def element_ids(self): # de-serializes the Revit native objects return [x.deserialize() for x in self._elmnt_ids] - - + + mydata = script.load_data("Selected Elements") mydata.element_ids ``` diff --git a/pyrevitlib/pyrevit/userconfig.py b/pyrevitlib/pyrevit/userconfig.py index 7af641dfa..94b84c0e9 100644 --- a/pyrevitlib/pyrevit/userconfig.py +++ b/pyrevitlib/pyrevit/userconfig.py @@ -302,6 +302,36 @@ def load_beta(self, state): value=state ) + @property + def output_close_others(self): + """Whether to close other output windows.""" + return self.core.get_option( + 'closeothers', + default_value=False, + ) + + @output_close_others.setter + def output_close_others(self, state): + self.core.set_option( + 'closeothers', + value=state + ) + + @property + def output_close_mode(self): + """Output window closing mode: 'current_command' or 'orphaned'.""" + return self.core.get_option( + 'closeomode', + default_value='current_command', + ) + + @output_close_mode.setter + def output_close_mode(self, mode): + self.core.set_option( + 'closeomode', + value=mode + ) + @property def cpython_engine_version(self): """CPython engine version to use.""" From ef4b916f2c77a3d47549f7a9546e91683408090e Mon Sep 17 00:00:00 2001 From: MohamedASLI Date: Wed, 3 Sep 2025 11:45:58 +0200 Subject: [PATCH 2/8] add resource dictionaries for close other consoles option --- .../SettingsWindow.ResourceDictionary.en_us.xaml | 10 ++++++++++ .../SettingsWindow.ResourceDictionary.fr_fr.xaml | 14 ++++++++++++-- .../SettingsWindow.ResourceDictionary.ru.xaml | 12 +++++++++++- .../Settings.smartbutton/SettingsWindow.xaml | 15 +++++++-------- .../pyRevit.panel/Settings.smartbutton/script.py | 1 + 5 files changed, 41 insertions(+), 11 deletions(-) diff --git a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.en_us.xaml b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.en_us.xaml index 6ca7ae96d..865bd358f 100644 --- a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.en_us.xaml +++ b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.en_us.xaml @@ -63,6 +63,16 @@ This helps to reduce the number of open output windows. + Current command consoles + + Close all console windows associated with the current running command. + + + All consoles + + Close all currently open console windows, even those that are not associated with the running command. + + Caching Reset Caching to default 200 diff --git a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.fr_fr.xaml b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.fr_fr.xaml index 294887d7c..51e79ae60 100644 --- a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.fr_fr.xaml +++ b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.fr_fr.xaml @@ -59,8 +59,18 @@ Fermer les consoles ouvertes - Si activé, pyRevit fermera les autres consoles de sortie lorsqu'un nouveau script est exécuté. - Cela aide à réduire le nombre de fenêtres de sortie ouvertes simultanément. + Si activé, pyRevit fermera les autres consoles de sortie lors de l'exécution d'un nouveau script. + Cela permet de réduire le nombre de fenêtres de sortie ouvertes. + + + Consoles de la commande actuelle + + Fermer toutes les fenêtres de console associées à la commande en cours d'exécution. + + + Toutes les consoles + + Fermer toutes les fenêtres de console actuellement ouvertes, même celles qui ne sont pas associées à la commande en cours. Mise en cache diff --git a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.ru.xaml b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.ru.xaml index 152f6eef4..4195ea3a3 100644 --- a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.ru.xaml +++ b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.ru.xaml @@ -61,7 +61,17 @@ Закрывать другие открытые консоли Если включено, pyRevit будет закрывать другие консоли вывода при запуске нового скрипта. - Это помогает уменьшить количество одновременно открытых окон вывода. + Это помогает уменьшить количество открытых окон вывода. + + + Консоли текущей команды + + Закрыть все окна консоли, связанные с текущей выполняемой командой. + + + Все консоли + + Закрыть все открытые в данный момент окна консоли, даже те, которые не связаны с выполняемой командой. Кеширование diff --git a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.xaml b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.xaml index 750a708bd..71f856b94 100644 --- a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.xaml +++ b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.xaml @@ -207,8 +207,8 @@ - - + + @@ -216,12 +216,11 @@ - - - - - - + + + + + diff --git a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/script.py b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/script.py index f3ad9a38b..b1ece4d8f 100644 --- a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/script.py +++ b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/script.py @@ -156,6 +156,7 @@ def _setup_core_options(self): self.minhostdrivefreespace_tb.Text = str(user_config.min_host_drivefreespace) self.loadbetatools_cb.IsChecked = user_config.load_beta + self.minimize_consoles_cb.IsChecked = user_config.output_close_others if user_config.output_close_mode == 'current_command': self.closewindows_current_rb.IsChecked = True From a05cfa7054f56303fbf17f3b0df09c7fa6a0171f Mon Sep 17 00:00:00 2001 From: MohamedASLI Date: Fri, 5 Sep 2025 10:48:30 +0200 Subject: [PATCH 3/8] rollback python get_output modification, implement changes at root in ScriptConsole.cs --- .../ScriptConsole.cs | 63 ++++++++++++++++++- ...ttingsWindow.ResourceDictionary.en_us.xaml | 4 +- ...ttingsWindow.ResourceDictionary.fr_fr.xaml | 4 +- .../SettingsWindow.ResourceDictionary.ru.xaml | 4 +- .../Settings.smartbutton/SettingsWindow.xaml | 4 +- .../Settings.smartbutton/script.py | 9 +-- pyrevitlib/pyrevit/script.py | 17 +---- pyrevitlib/pyrevit/userconfig.py | 24 +++++-- 8 files changed, 94 insertions(+), 35 deletions(-) diff --git a/dev/pyRevitLabs.PyRevit.Runtime/ScriptConsole.cs b/dev/pyRevitLabs.PyRevit.Runtime/ScriptConsole.cs index 8e71bde5d..470a4ac9c 100644 --- a/dev/pyRevitLabs.PyRevit.Runtime/ScriptConsole.cs +++ b/dev/pyRevitLabs.PyRevit.Runtime/ScriptConsole.cs @@ -376,6 +376,64 @@ public string GetFullHtml() { return ScriptConsoleConfigs.DOCTYPE + head.OuterHtml + ActiveDocument.Body.OuterHtml; } + private static (bool closeOthers, string closeMode) ReadCloseOtherOutputsSetting() { + try { + var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); + var cfg = System.IO.Path.Combine(appData, "pyRevit", "pyRevit_config.ini"); + if (!System.IO.File.Exists(cfg)) + return (false, "current_command"); + + bool inCore = false; + bool close = false; + string mode = "current_command"; + foreach (var raw in System.IO.File.ReadAllLines(cfg)) { + var line = raw.Trim(); + if (line.Length == 0 || line.StartsWith("#") || line.StartsWith(";")) + continue; + + if (line == "[core]") { inCore = true; continue; } + if (line.StartsWith("[") && line.EndsWith("]")) { inCore = false; continue; } + + if (!inCore) continue; + + if (line.StartsWith("close_other_outputs", StringComparison.InvariantCultureIgnoreCase)) + close = line.IndexOf("true", StringComparison.InvariantCultureIgnoreCase) >= 0; + + else if (line.StartsWith("close_mode", StringComparison.InvariantCultureIgnoreCase)) { + var parts = line.Split(new[] { '=' }, 2); + if (parts.Length == 2) + mode = parts[1].Trim().Trim('"'); + } + } + return (close, mode); + } + catch { + return (false, "current_command"); + } + } + + public void CloseOtherOutputs(bool filterByCommandId = true) { + try { + var filterId = filterByCommandId ? this.OutputId : null; + ScriptConsoleManager.CloseActiveOutputWindows(excludeOutputWindow: this, filterOutputWindowId: filterId); + } + catch { + } + } + + private void ApplyCloseOtherOutputsIfConfigured() { + var (close, mode) = ReadCloseOtherOutputsSetting(); + if (!close) return; + + this.Dispatcher.BeginInvoke(new Action(() => { + if (string.Equals(mode, "current_command", StringComparison.InvariantCultureIgnoreCase)) + CloseOtherOutputs(filterByCommandId: true); + else if (string.Equals(mode, "close_all", StringComparison.InvariantCultureIgnoreCase)) { + CloseOtherOutputs(filterByCommandId: false); + } + })); + } + private void SetupDefaultPage(string styleSheetFilePath = null) { string cssFilePath; if (styleSheetFilePath != null) @@ -448,7 +506,7 @@ public void FocusOutput() { public System.Windows.Forms.HtmlElement ComposeEntry(string contents, string HtmlElementType) { WaitReadyBrowser(); - + // order is important // "<" ---> < contents = ScriptConsoleConfigs.EscapeForHtml(contents); @@ -552,7 +610,7 @@ public string GetInput() { dbgMode = true; } } - + // if no debugger, find other patterns if (!dbgMode && new string[] { "select", "file" }.All(x => lastLine.Contains(x))) @@ -782,6 +840,7 @@ public void SelfDestructTimer(int seconds) { private void Window_Loaded(object sender, System.EventArgs e) { var outputWindow = (ScriptConsole)sender; ScriptConsoleManager.AppendToOutputWindowList(this); + ApplyCloseOtherOutputsIfConfigured(); } private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { diff --git a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.en_us.xaml b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.en_us.xaml index 865bd358f..c56b6907f 100644 --- a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.en_us.xaml +++ b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.en_us.xaml @@ -68,8 +68,8 @@ Close all console windows associated with the current running command. - All consoles - + All consoles + Close all currently open console windows, even those that are not associated with the running command. diff --git a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.fr_fr.xaml b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.fr_fr.xaml index 51e79ae60..8f573b702 100644 --- a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.fr_fr.xaml +++ b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.fr_fr.xaml @@ -68,8 +68,8 @@ Fermer toutes les fenêtres de console associées à la commande en cours d'exécution. - Toutes les consoles - + Toutes les consoles + Fermer toutes les fenêtres de console actuellement ouvertes, même celles qui ne sont pas associées à la commande en cours. diff --git a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.ru.xaml b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.ru.xaml index 4195ea3a3..dffc41428 100644 --- a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.ru.xaml +++ b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.ru.xaml @@ -69,8 +69,8 @@ Закрыть все окна консоли, связанные с текущей выполняемой командой. - Все консоли - + Все консоли + Закрыть все открытые в данный момент окна консоли, даже те, которые не связаны с выполняемой командой. diff --git a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.xaml b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.xaml index 71f856b94..3ddf230fe 100644 --- a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.xaml +++ b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.xaml @@ -219,8 +219,8 @@ - - + + diff --git a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/script.py b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/script.py index b1ece4d8f..d48654035 100644 --- a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/script.py +++ b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/script.py @@ -158,12 +158,13 @@ def _setup_core_options(self): self.loadbetatools_cb.IsChecked = user_config.load_beta self.minimize_consoles_cb.IsChecked = user_config.output_close_others + if user_config.output_close_mode == 'current_command': self.closewindows_current_rb.IsChecked = True - self.closewindows_orphaned_rb.IsChecked = False - else: # 'orphaned' + self.closewindows_close_all_rb.IsChecked = False + else: # 'close_all' self.closewindows_current_rb.IsChecked = False - self.closewindows_orphaned_rb.IsChecked = True + self.closewindows_close_all_rb.IsChecked = True def _setup_engines(self): """Sets up the list of available engines.""" @@ -858,7 +859,7 @@ def _save_core_options(self): if self.closewindows_current_rb.IsChecked: user_config.output_close_mode = 'current_command' else: - user_config.output_close_mode = 'orphaned' + user_config.output_close_mode = 'close_all' def _save_engines(self): # set active cpython engine diff --git a/pyrevitlib/pyrevit/script.py b/pyrevitlib/pyrevit/script.py index 0d5892060..1fc1bd29f 100644 --- a/pyrevitlib/pyrevit/script.py +++ b/pyrevitlib/pyrevit/script.py @@ -154,22 +154,7 @@ def get_output(): Returns: (pyrevit.output.PyRevitOutputWindow): Output wrapper object """ - from pyrevit.userconfig import user_config - - # Get the output window using the output module's get_output() function - output_window = output.get_output() - - # Apply user configuration for output window management - if user_config.output_close_others: - try: - if user_config.output_close_mode == 'current_command': - output_window.close_others() - else: - output_window.close_others(True) - except: - pass # Silently fail if not supported - - return output_window + return output.get_output() def get_config(section=None): diff --git a/pyrevitlib/pyrevit/userconfig.py b/pyrevitlib/pyrevit/userconfig.py index 94b84c0e9..95c31a2d5 100644 --- a/pyrevitlib/pyrevit/userconfig.py +++ b/pyrevitlib/pyrevit/userconfig.py @@ -50,7 +50,21 @@ from pyrevit.coreutils import configparser from pyrevit.coreutils import logger from pyrevit.versionmgr import upgrade +# pylint: disable=C0103,C0413,W0703 +import os +import os.path as op +from pyrevit import EXEC_PARAMS, HOME_DIR, HOST_APP +from pyrevit import EXTENSIONS_DEFAULT_DIR, THIRDPARTY_EXTENSIONS_DEFAULT_DIR +from pyrevit import PYREVIT_ALLUSER_APP_DIR, PYREVIT_APP_DIR +from pyrevit import PyRevitException +from pyrevit import coreutils +from pyrevit.compat import winreg as wr +from pyrevit.coreutils import appdata +from pyrevit.coreutils import configparser +from pyrevit.coreutils import logger +from pyrevit.labs import PyRevit +from pyrevit.versionmgr import upgrade DEFAULT_CSV_SEPARATOR = ',' @@ -306,29 +320,29 @@ def load_beta(self, state): def output_close_others(self): """Whether to close other output windows.""" return self.core.get_option( - 'closeothers', + 'close_other_outputs', default_value=False, ) @output_close_others.setter def output_close_others(self, state): self.core.set_option( - 'closeothers', + 'close_other_outputs', value=state ) @property def output_close_mode(self): - """Output window closing mode: 'current_command' or 'orphaned'.""" + """Output window closing mode: 'current_command' or 'close_all'.""" return self.core.get_option( - 'closeomode', + 'close_mode', default_value='current_command', ) @output_close_mode.setter def output_close_mode(self, mode): self.core.set_option( - 'closeomode', + 'close_mode', value=mode ) From 7a1362140fb20935a7bd7e6c1fbc9047d834d400 Mon Sep 17 00:00:00 2001 From: MohamedASLI Date: Fri, 5 Sep 2025 11:17:16 +0200 Subject: [PATCH 4/8] add clean engine run to avoid inconsistent config options between multiple revit instances --- .../pyRevit.tab/pyRevit.panel/Settings.smartbutton/bundle.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/bundle.yaml b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/bundle.yaml index 37a2fc2e5..c8ac032b6 100644 --- a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/bundle.yaml +++ b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/bundle.yaml @@ -38,3 +38,5 @@ tooltip: Zeigt die Konfigurationsdatei im Explorer an. context: zero-doc +engine: + clean: true From a2c9c2b526b42f3667b634164d79b7b04d49ac7e Mon Sep 17 00:00:00 2001 From: MohamedASLI Date: Fri, 5 Sep 2025 11:51:57 +0200 Subject: [PATCH 5/8] update option description in UI --- .../SettingsWindow.ResourceDictionary.en_us.xaml | 3 ++- .../SettingsWindow.ResourceDictionary.fr_fr.xaml | 3 ++- .../SettingsWindow.ResourceDictionary.ru.xaml | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.en_us.xaml b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.en_us.xaml index c56b6907f..e27fcb3e4 100644 --- a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.en_us.xaml +++ b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.en_us.xaml @@ -60,7 +60,8 @@ Close other open consoles If enabled, pyRevit will close other output consoles when a new script is run. - This helps to reduce the number of open output windows. + This helps to reduce the number of open output windows. Activate this option, unless you want to compare + multiple output data. Current command consoles diff --git a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.fr_fr.xaml b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.fr_fr.xaml index 8f573b702..f9aad5c86 100644 --- a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.fr_fr.xaml +++ b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.fr_fr.xaml @@ -60,7 +60,8 @@ Fermer les consoles ouvertes Si activé, pyRevit fermera les autres consoles de sortie lors de l'exécution d'un nouveau script. - Cela permet de réduire le nombre de fenêtres de sortie ouvertes. + Cela permet de réduire le nombre de fenêtres de sortie ouvertes. Activez cette option, sauf si vous + voulez comparez les sorties de plusieurs scripts. Consoles de la commande actuelle diff --git a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.ru.xaml b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.ru.xaml index dffc41428..dc1f57968 100644 --- a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.ru.xaml +++ b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.ru.xaml @@ -61,7 +61,8 @@ Закрывать другие открытые консоли Если включено, pyRevit будет закрывать другие консоли вывода при запуске нового скрипта. - Это помогает уменьшить количество открытых окон вывода. + Это помогает уменьшить количество открытых окон вывода. Активируйте эту опцию, если вам + не требуется сравнивать данные из нескольких окон вывода. Консоли текущей команды From 683bbc3fdac83abf208fc93d2053730fce6327c2 Mon Sep 17 00:00:00 2001 From: Jean-Marc Couffin Date: Tue, 9 Sep 2025 10:19:46 +0200 Subject: [PATCH 6/8] Update SettingsWindow.ResourceDictionary.fr_fr.xaml --- .../SettingsWindow.ResourceDictionary.fr_fr.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.fr_fr.xaml b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.fr_fr.xaml index f9aad5c86..281d8e5de 100644 --- a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.fr_fr.xaml +++ b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/SettingsWindow.ResourceDictionary.fr_fr.xaml @@ -61,7 +61,7 @@ Si activé, pyRevit fermera les autres consoles de sortie lors de l'exécution d'un nouveau script. Cela permet de réduire le nombre de fenêtres de sortie ouvertes. Activez cette option, sauf si vous - voulez comparez les sorties de plusieurs scripts. + voulez comparer les sorties de plusieurs scripts. Consoles de la commande actuelle From c61c75250e9ee2a70967e8477869e577a899bc37 Mon Sep 17 00:00:00 2001 From: Mohamed Asli <75548741+MohamedAsli@users.noreply.github.com> Date: Tue, 9 Sep 2025 16:19:48 +0200 Subject: [PATCH 7/8] remove duplicate imports from userconfig Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- pyrevitlib/pyrevit/userconfig.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/pyrevitlib/pyrevit/userconfig.py b/pyrevitlib/pyrevit/userconfig.py index 95c31a2d5..fa532c684 100644 --- a/pyrevitlib/pyrevit/userconfig.py +++ b/pyrevitlib/pyrevit/userconfig.py @@ -51,21 +51,6 @@ from pyrevit.coreutils import logger from pyrevit.versionmgr import upgrade # pylint: disable=C0103,C0413,W0703 -import os -import os.path as op - -from pyrevit import EXEC_PARAMS, HOME_DIR, HOST_APP -from pyrevit import EXTENSIONS_DEFAULT_DIR, THIRDPARTY_EXTENSIONS_DEFAULT_DIR -from pyrevit import PYREVIT_ALLUSER_APP_DIR, PYREVIT_APP_DIR -from pyrevit import PyRevitException -from pyrevit import coreutils -from pyrevit.compat import winreg as wr -from pyrevit.coreutils import appdata -from pyrevit.coreutils import configparser -from pyrevit.coreutils import logger -from pyrevit.labs import PyRevit -from pyrevit.versionmgr import upgrade - DEFAULT_CSV_SEPARATOR = ',' From 2be77438c4ff4e536d2c79de113680e39509caf6 Mon Sep 17 00:00:00 2001 From: MohamedASLI Date: Fri, 12 Sep 2025 05:08:21 +0200 Subject: [PATCH 8/8] add config consts and enum comparison --- .../ScriptConsole.cs | 60 ++++--------------- .../pyRevitLabs.PyRevit/PyRevitConfigs.cs | 45 ++++++++++++++ .../pyRevitLabs.PyRevit/PyRevitConsts.cs | 10 +++- .../Settings.smartbutton/script.py | 9 +-- pyrevitlib/pyrevit/userconfig.py | 44 +++++++++----- 5 files changed, 100 insertions(+), 68 deletions(-) diff --git a/dev/pyRevitLabs.PyRevit.Runtime/ScriptConsole.cs b/dev/pyRevitLabs.PyRevit.Runtime/ScriptConsole.cs index 470a4ac9c..9068ee6c5 100644 --- a/dev/pyRevitLabs.PyRevit.Runtime/ScriptConsole.cs +++ b/dev/pyRevitLabs.PyRevit.Runtime/ScriptConsole.cs @@ -14,6 +14,7 @@ using pyRevitLabs.Common; using pyRevitLabs.CommonWPF.Controls; using pyRevitLabs.Emojis; +using pyRevitLabs.PyRevit; namespace PyRevitLabs.PyRevit.Runtime { public struct ScriptConsoleDebugger { @@ -171,7 +172,7 @@ public partial class ScriptConsole : ScriptConsoleTemplate, IComponentConnector, private System.Windows.Forms.HtmlElement _lastDocumentBody = null; private UIApplication _uiApp; - private List _supportedDebuggers = + private List _supportedDebuggers = new List { new ScriptConsoleDebugger() { Name = "Pdb (IronPython|CPython)", @@ -376,39 +377,15 @@ public string GetFullHtml() { return ScriptConsoleConfigs.DOCTYPE + head.OuterHtml + ActiveDocument.Body.OuterHtml; } - private static (bool closeOthers, string closeMode) ReadCloseOtherOutputsSetting() { - try { - var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); - var cfg = System.IO.Path.Combine(appData, "pyRevit", "pyRevit_config.ini"); - if (!System.IO.File.Exists(cfg)) - return (false, "current_command"); - - bool inCore = false; - bool close = false; - string mode = "current_command"; - foreach (var raw in System.IO.File.ReadAllLines(cfg)) { - var line = raw.Trim(); - if (line.Length == 0 || line.StartsWith("#") || line.StartsWith(";")) - continue; - - if (line == "[core]") { inCore = true; continue; } - if (line.StartsWith("[") && line.EndsWith("]")) { inCore = false; continue; } - - if (!inCore) continue; - - if (line.StartsWith("close_other_outputs", StringComparison.InvariantCultureIgnoreCase)) - close = line.IndexOf("true", StringComparison.InvariantCultureIgnoreCase) >= 0; - - else if (line.StartsWith("close_mode", StringComparison.InvariantCultureIgnoreCase)) { - var parts = line.Split(new[] { '=' }, 2); - if (parts.Length == 2) - mode = parts[1].Trim().Trim('"'); - } - } - return (close, mode); - } - catch { - return (false, "current_command"); + private void ApplyCloseOthersConfig() + { + if (PyRevitConfigs.GetCloseOtherOutputs()) + { + var mode = PyRevitConfigs.GetCloseOutputMode(); + this.Dispatcher.BeginInvoke(new Action(() => + { + CloseOtherOutputs(filterByCommandId: mode == OutputCloseMode.CurrentCommand); + })); } } @@ -421,19 +398,6 @@ public void CloseOtherOutputs(bool filterByCommandId = true) { } } - private void ApplyCloseOtherOutputsIfConfigured() { - var (close, mode) = ReadCloseOtherOutputsSetting(); - if (!close) return; - - this.Dispatcher.BeginInvoke(new Action(() => { - if (string.Equals(mode, "current_command", StringComparison.InvariantCultureIgnoreCase)) - CloseOtherOutputs(filterByCommandId: true); - else if (string.Equals(mode, "close_all", StringComparison.InvariantCultureIgnoreCase)) { - CloseOtherOutputs(filterByCommandId: false); - } - })); - } - private void SetupDefaultPage(string styleSheetFilePath = null) { string cssFilePath; if (styleSheetFilePath != null) @@ -840,7 +804,7 @@ public void SelfDestructTimer(int seconds) { private void Window_Loaded(object sender, System.EventArgs e) { var outputWindow = (ScriptConsole)sender; ScriptConsoleManager.AppendToOutputWindowList(this); - ApplyCloseOtherOutputsIfConfigured(); + ApplyCloseOthersConfig(); } private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { diff --git a/dev/pyRevitLabs/pyRevitLabs.PyRevit/PyRevitConfigs.cs b/dev/pyRevitLabs/pyRevitLabs.PyRevit/PyRevitConfigs.cs index 281f3c0f6..3b13eed9e 100644 --- a/dev/pyRevitLabs/pyRevitLabs.PyRevit/PyRevitConfigs.cs +++ b/dev/pyRevitLabs/pyRevitLabs.PyRevit/PyRevitConfigs.cs @@ -35,6 +35,12 @@ public enum PyRevitLogLevels Debug } + public enum OutputCloseMode + { + CurrentCommand, + CloseAll + } + public static class PyRevitConfigs { private static readonly Logger logger = LogManager.GetCurrentClassLogger(); @@ -505,6 +511,45 @@ public static void SetLoadBetaTools(bool state) cfg.SetValue(PyRevitConsts.ConfigsCoreSection, PyRevitConsts.ConfigsLoadBetaKey, state); } + // close other outputs config + public static bool GetCloseOtherOutputs() + { + var cfg = GetConfigFile(); + var status = cfg.GetValue(PyRevitConsts.ConfigsCoreSection, PyRevitConsts.ConfigsCloseOtherOutputsKey); + return status != null ? bool.Parse(status) : PyRevitConsts.ConfigsCloseOtherOutputsDefault; + } + + public static void SetCloseOtherOutputs(bool state) + { + var cfg = GetConfigFile(); + cfg.SetValue(PyRevitConsts.ConfigsCoreSection, PyRevitConsts.ConfigsCloseOtherOutputsKey, state); + } + + public static OutputCloseMode GetCloseOutputMode() + { + var cfg = GetConfigFile(); + var raw = cfg.GetValue(PyRevitConsts.ConfigsCoreSection, PyRevitConsts.ConfigsCloseOutputModeKey); + + var s = (raw ?? PyRevitConsts.ConfigsCloseOutputModeDefault).Trim().Trim('"', '\''); + + if (s.Equals(PyRevitConsts.ConfigsCloseOutputModeCloseAll, StringComparison.InvariantCultureIgnoreCase)) + { + return OutputCloseMode.CloseAll; + } + + return OutputCloseMode.CurrentCommand; + } + + public static void SetCloseOutputMode(OutputCloseMode mode) + { + var cfg = GetConfigFile(); + var value = (mode == OutputCloseMode.CloseAll) + ? PyRevitConsts.ConfigsCloseOutputModeCloseAll + : PyRevitConsts.ConfigsCloseOutputModeCurrentCommand; + + cfg.SetValue(PyRevitConsts.ConfigsCoreSection, PyRevitConsts.ConfigsCloseOutputModeKey, value); + } + // cpythonengine public static int GetCpythonEngineVersion() { diff --git a/dev/pyRevitLabs/pyRevitLabs.PyRevit/PyRevitConsts.cs b/dev/pyRevitLabs/pyRevitLabs.PyRevit/PyRevitConsts.cs index 7a9379536..2952412b6 100644 --- a/dev/pyRevitLabs/pyRevitLabs.PyRevit/PyRevitConsts.cs +++ b/dev/pyRevitLabs/pyRevitLabs.PyRevit/PyRevitConsts.cs @@ -42,7 +42,7 @@ public static class PyRevitConsts { public const string ReleaseDirName = "release"; public const string SitePackagesDirName = "site-packages"; public const string PyRevitfileFilename = "PyRevitfile"; - + public const string NetFxFolder = "netfx"; public const string NetCoreFolder = "netcore"; @@ -85,6 +85,12 @@ public static class PyRevitConsts { public const int ConfigsMinDriveSpaceDefault = 0; public const string ConfigsLoadBetaKey = "loadbeta"; public const bool ConfigsLoadBetaDefault = false; + public const string ConfigsCloseOtherOutputsKey = "closeotheroutputs"; + public const bool ConfigsCloseOtherOutputsDefault = false; + public const string ConfigsCloseOutputModeKey = "closeoutputmode"; + public const string ConfigsCloseOutputModeDefault = "currentcommand"; + public const string ConfigsCloseOutputModeCurrentCommand = "currentcommand"; + public const string ConfigsCloseOutputModeCloseAll = "closeall"; public const string ConfigsCPythonEngineKey = "cpyengine"; public const int ConfigsCPythonEngineDefault = 0; public const string ConfigsLocaleKey = "user_locale"; @@ -179,7 +185,7 @@ public static class PyRevitConsts { public static string FindConfigFileInDirectory(string sourcePath) { var configMatcher = new Regex(ConfigsFileRegexPattern, RegexOptions.IgnoreCase); // capture exceptions that might occur getting the files under sourcePath - // + // try { if (CommonUtils.VerifyPath(sourcePath)) foreach (string subFile in Directory.GetFiles(sourcePath)) diff --git a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/script.py b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/script.py index d48654035..6477d9524 100644 --- a/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/script.py +++ b/extensions/pyRevitCore.extension/pyRevit.tab/pyRevit.panel/Settings.smartbutton/script.py @@ -159,10 +159,11 @@ def _setup_core_options(self): self.minimize_consoles_cb.IsChecked = user_config.output_close_others - if user_config.output_close_mode == 'current_command': + mode = user_config.output_close_mode_enum + if mode == PyRevit.OutputCloseMode.CurrentCommand: self.closewindows_current_rb.IsChecked = True self.closewindows_close_all_rb.IsChecked = False - else: # 'close_all' + else: self.closewindows_current_rb.IsChecked = False self.closewindows_close_all_rb.IsChecked = True @@ -857,9 +858,9 @@ def _save_core_options(self): user_config.output_close_others = self.minimize_consoles_cb.IsChecked if self.closewindows_current_rb.IsChecked: - user_config.output_close_mode = 'current_command' + user_config.output_close_mode_enum = PyRevit.OutputCloseMode.CurrentCommand else: - user_config.output_close_mode = 'close_all' + user_config.output_close_mode_enum = PyRevit.OutputCloseMode.CloseAll def _save_engines(self): # set active cpython engine diff --git a/pyrevitlib/pyrevit/userconfig.py b/pyrevitlib/pyrevit/userconfig.py index fa532c684..8363a4610 100644 --- a/pyrevitlib/pyrevit/userconfig.py +++ b/pyrevitlib/pyrevit/userconfig.py @@ -305,31 +305,47 @@ def load_beta(self, state): def output_close_others(self): """Whether to close other output windows.""" return self.core.get_option( - 'close_other_outputs', - default_value=False, + CONSTS.ConfigsCloseOtherOutputsKey, + default_value=CONSTS.ConfigsCloseOtherOutputsDefault, ) @output_close_others.setter def output_close_others(self, state): self.core.set_option( - 'close_other_outputs', + CONSTS.ConfigsCloseOtherOutputsKey, value=state ) @property - def output_close_mode(self): - """Output window closing mode: 'current_command' or 'close_all'.""" - return self.core.get_option( - 'close_mode', - default_value='current_command', + def output_close_mode_enum(self): + """Output window closing mode as enum (CurrentCommand | CloseAll).""" + value = self.core.get_option( + CONSTS.ConfigsCloseOutputModeKey, + default_value=CONSTS.ConfigsCloseOutputModeDefault, ) + if not value: + value = CONSTS.ConfigsCloseOutputModeDefault - @output_close_mode.setter - def output_close_mode(self, mode): - self.core.set_option( - 'close_mode', - value=mode - ) + value_lc = str(value).lower() + + if value_lc == str(CONSTS.ConfigsCloseOutputModeCloseAll).lower(): + return PyRevit.OutputCloseMode.CloseAll + else: + return PyRevit.OutputCloseMode.CurrentCommand + + @output_close_mode_enum.setter + def output_close_mode_enum(self, mode): + """Store string in INI, mapped from enum.""" + if mode == PyRevit.OutputCloseMode.CloseAll: + self.core.set_option( + CONSTS.ConfigsCloseOutputModeKey, + value=CONSTS.ConfigsCloseOutputModeCloseAll + ) + else: + self.core.set_option( + CONSTS.ConfigsCloseOutputModeKey, + value=CONSTS.ConfigsCloseOutputModeCurrentCommand + ) @property def cpython_engine_version(self):