diff --git a/devsimpy/DetachedFrame.py b/devsimpy/DetachedFrame.py index 27ad4627..cd508443 100644 --- a/devsimpy/DetachedFrame.py +++ b/devsimpy/DetachedFrame.py @@ -106,7 +106,7 @@ def __init__(self, parent=None, ID=wx.NewIdRef(), title="", diagram=None, name=" self.toggle_list = getTopLevelWindow().toggle_list else: sys.stdout.write(_('Alone mode for DetachedFrame: Connector buttons are not binded\n')) - self.toggle_list = [wx.NewIdRef() for i in range(6)] + self.toggle_list = [wx.NewIdRef() for i in range(7)] self.tools = [ toolbar.AddTool(Menu.ID_SAVE, "", load_and_resize_image('save.png'), wx.NullBitmap, shortHelp=_('Save File') ,longHelp=_('Save the current diagram'), clientData=self.canvas), toolbar.AddTool(Menu.ID_SAVEAS, "", load_and_resize_image('save_as.png'), wx.NullBitmap, shortHelp=_('Save File As'), longHelp=_('Save the diagram with an another name'), clientData=self.canvas), diff --git a/devsimpy/PreferencesGUI.py b/devsimpy/PreferencesGUI.py index 81f361df..b79b5816 100644 --- a/devsimpy/PreferencesGUI.py +++ b/devsimpy/PreferencesGUI.py @@ -985,43 +985,173 @@ def __init__(self, parent, title): def InitUI(self): """ Init the UI. """ - _icon = wx.Icon() - _icon.CopyFromBitmap(load_and_resize_image("preferences.png")) - self.SetIcon(_icon) - + icon = wx.Icon() + icon.CopyFromBitmap(load_and_resize_image("preferences.png")) + self.SetIcon(icon) + self.SetMinSize((400,500)) - - ### Panel + panel = wx.Panel(self, wx.NewIdRef()) self.pref = Preferences(panel) - - ### Buttons + + # Boutons Apply et Cancel self.cancel = wx.Button(panel, wx.ID_CANCEL) self.apply = wx.Button(panel, wx.ID_OK) - + + # Bouton d'aide générale (NOUVEAU) + self.help_btn = wx.Button(panel, wx.ID_HELP, "?") + self.help_btn.SetToolTip(_("Show help about preferences")) + self.apply.SetToolTipString = self.apply.SetToolTip self.cancel.SetToolTipString = self.cancel.SetToolTip - + self.apply.SetToolTipString(_("Apply all changing")) self.cancel.SetToolTipString(_("Cancel without changing")) + self.apply.SetDefault() - - ### Sizers - vsizer = wx.BoxSizer(wx.VERTICAL) - hsizer = wx.BoxSizer(wx.HORIZONTAL) - - hsizer.Add(self.cancel, 0) - hsizer.Add(self.apply, 0, wx.EXPAND|wx.LEFT, 5) - vsizer.Add(self.pref, 1, wx.ALL|wx.EXPAND, 5) - vsizer.Add(hsizer, 0, wx.ALL|wx.ALIGN_RIGHT, 5) - - panel.SetSizer(vsizer) - vsizer.Fit(panel) - - ### Binding + + # Layout + vbox = wx.BoxSizer(wx.VERTICAL) + hbox_buttons = wx.BoxSizer(wx.HORIZONTAL) + + hbox_buttons.Add(self.help_btn, 0, wx.ALL, 5) + hbox_buttons.AddStretchSpacer() + hbox_buttons.Add(self.cancel, 0, wx.ALL, 5) + hbox_buttons.Add(self.apply, 0, wx.ALL, 5) + + vbox.Add(self.pref, 1, wx.EXPAND | wx.ALL, 10) + vbox.Add(hbox_buttons, 0, wx.EXPAND | wx.ALL, 5) + + panel.SetSizer(vbox) + + # Binding + self.Bind(wx.EVT_BUTTON, self.OnShowPreferencesHelp, id=wx.ID_HELP) self.Bind(wx.EVT_BUTTON, self.OnApply, id=wx.ID_OK) self.Bind(wx.EVT_BUTTON, self.OnCancel, id=wx.ID_CANCEL) - self.Bind(wx.EVT_BUTTON, self.OnClose, id=wx.ID_CLOSE) + + + def OnShowPreferencesHelp(self, event): + """Show help about preferences dialog""" + + help_msg = _( + "DEVSimPy PREFERENCES\n\n" + "═══════════════════════════════════════\n\n" + "This dialog allows you to configure DEVSimPy settings.\n" + "Navigate through tabs to access different preference categories.\n\n" + "═══════════════════════════════════════\n\n" + "GENERAL TAB:\n\n" + "• Plug-ins Directory: Location of DEVSimPy plugins\n" + " Change to use custom plugin collections\n\n" + "• Library Directory: Location of DEVS model libraries\n" + " Main directory containing all model libraries\n\n" + "• Output Directory: Where simulation outputs are saved\n" + " Results, logs, and generated files location\n\n" + "• Number of Recent Files: Length of recent files list\n" + " Range: 2-20 files\n\n" + "• Font Size: Size of text in block diagrams\n" + " Range: 2-20 points\n\n" + "• Deep of History: Number of undo/redo levels\n" + " Range: 2-100 operations\n\n" + "• wxPython Version: Select wxPython version to use\n" + " Requires restart to take effect\n\n" + "• Transparency: Enable transparency for detached frames\n" + " Makes windows semi-transparent when moving\n\n" + "• Notifications: Enable notification messages\n" + " Show pop-up notifications for events\n\n" + "═══════════════════════════════════════\n\n" + "SIMULATION TAB:\n\n" + "• DEVS Kernel: Choose between PyDEVS and PyPDEVS\n" + " PyDEVS: Classic DEVS simulator\n" + " PyPDEVS: Parallel DEVS simulator (recommended)\n\n" + "• Default Strategy: Simulation algorithm to use\n" + " - original: Standard DEVS algorithm\n" + " - bag: Optimized message handling\n" + " - direct: Direct coupling (fastest)\n\n" + "• Sound on Success: Play sound when simulation completes\n" + " Select custom MP3/WAV file\n\n" + "• Sound on Error: Play sound when simulation fails\n" + " Helps detect problems quickly\n\n" + "• No Time Limit: Default state for NTL checkbox\n" + " Run simulations until all models inactive\n\n" + "• Plot Dynamic Frequency: How often to update plots\n" + " Higher = more frequent updates (slower)\n" + " Lower = less frequent updates (faster)\n\n" + "═══════════════════════════════════════\n\n" + "EDITOR TAB:\n\n" + "• Use DEVSimPy Local Editor: Use built-in code editor\n" + " When checked: Use internal editor\n" + " When unchecked: Use external editor\n\n" + "• Select External Editor: Choose external code editor\n" + " Available: Spyder, Pyzo, or others\n" + " Click Refresh to detect newly installed editors\n\n" + "Note: External editor must be installed separately\n" + "Use 'Update' button to install missing editors\n\n" + "═══════════════════════════════════════\n\n" + "AI TAB:\n\n" + "• Select an AI: Choose AI code generation service\n" + " - ChatGPT: OpenAI's language model\n" + " - Ollama: Local AI models\n\n" + "• API Key (ChatGPT): Your OpenAI API key\n" + " Get from: https://platform.openai.com/api-keys\n" + " Stored securely, never shared\n\n" + "• Port (Ollama): Local Ollama server port\n" + " Default: 11434\n" + " Ollama must be running locally\n\n" + "• Check Button: Verify AI configuration\n" + " Tests connection and validates settings\n\n" + "• Info Button: Open AI documentation\n" + " Learn more about selected AI service\n\n" + "═══════════════════════════════════════\n\n" + "PLUGINS TAB:\n\n" + "• Plugin List: All available DEVSimPy plugins\n" + " Checkbox = enabled/disabled state\n\n" + "• Add (+): Install new plugins\n" + " Browse for plugin files to add\n\n" + "• Delete (-): Remove all plugins\n" + " Warning: Deletes all plugin files!\n\n" + "• Refresh (⟳): Update plugin list\n" + " Detects newly added plugins\n\n" + "Double-click plugin name to enable/disable\n" + "Changes take effect after restart\n\n" + "═══════════════════════════════════════\n\n" + "APPLYING CHANGES:\n\n" + "• Apply: Save all changes and close dialog\n" + " Settings are written to .devsimpy config file\n\n" + "• Cancel: Discard changes and close\n" + " All modifications are lost\n\n" + "Some changes require DEVSimPy restart:\n" + "- wxPython version change\n" + "- DEVS kernel change (PyDEVS ↔ PyPDEVS)\n" + "- Plugin enable/disable\n" + "- Library directory change\n\n" + "═══════════════════════════════════════\n\n" + "TIPS:\n\n" + "- Save preferences before closing DEVSimPy\n" + "- Test AI configuration before using code generation\n" + "- Increase undo history for complex modeling\n" + "- Use PyPDEVS for better performance\n" + "- External editors provide better code assistance\n" + "- Plugins extend DEVSimPy functionality\n" + "- Check 'Output Directory' has write permissions" + ) + + try: + import wx.lib.dialogs + dlg = wx.lib.dialogs.ScrolledMessageDialog( + self, + help_msg, + _("Preferences Help"), + size=(700, 650) + ) + dlg.ShowModal() + dlg.Destroy() + except Exception as e: + # Fallback + wx.MessageBox( + help_msg, + _("Preferences Help"), + wx.OK | wx.ICON_INFORMATION + ) def OnApply(self, evt): """ Apply button has been clicked. diff --git a/devsimpy/StandaloneGUI.py b/devsimpy/StandaloneGUI.py index 0d925c62..b289f527 100644 --- a/devsimpy/StandaloneGUI.py +++ b/devsimpy/StandaloneGUI.py @@ -113,10 +113,9 @@ def InitUI(self): icon = wx.Icon() icon.CopyFromBitmap(load_and_resize_image("properties.png")) self.SetIcon(icon) - + # Taille adaptée au contenu - self.SetSize((650, 500)) - # self.SetMinSize((600, 450)) + self.SetSize((650, 550)) panel = wx.Panel(self) panel.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) @@ -124,6 +123,24 @@ def InitUI(self): # Sizer principal avec marges uniformes main_sizer = wx.BoxSizer(wx.VERTICAL) + # --- En-tête avec titre et bouton d'aide (NOUVEAU) --- + header_sizer = wx.BoxSizer(wx.HORIZONTAL) + + title_label = wx.StaticText(panel, label=_("Standalone Package Generator")) + title_font = title_label.GetFont() + title_font.PointSize += 2 + title_font = title_font.Bold() + title_label.SetFont(title_font) + + help_btn = wx.Button(panel, wx.ID_HELP, "?", size=(30, 30)) + help_btn.SetToolTip(_("Show help about standalone package generation")) + + header_sizer.Add(title_label, 0, wx.ALIGN_CENTER_VERTICAL) + header_sizer.AddStretchSpacer() + header_sizer.Add(help_btn, 0, wx.ALIGN_CENTER_VERTICAL) + + main_sizer.Add(header_sizer, 0, wx.ALL|wx.EXPAND, border=15) + # --- Section Package Configuration --- config_box = wx.StaticBoxSizer(wx.VERTICAL, panel, _("Package Configuration")) @@ -137,8 +154,8 @@ def InitUI(self): config_grid.Add(filename_label, flag=wx.ALIGN_CENTER_VERTICAL) self._tc = wx.TextCtrl(panel, -1, - f"{self.yaml_model_name}-nogui-pkg.zip", - validator=ZipNameValidator()) + f"{self.yaml_model_name}-nogui-pkg.zip", + validator=ZipNameValidator()) self._tc.SetToolTip(_("Must end with .zip")) config_grid.Add(self._tc, flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) @@ -161,7 +178,7 @@ def InitUI(self): # Format format_label = wx.StaticText(panel, -1, _("Format:")) format_label.SetToolTip(_("The minimal format includes only necessary library files.\n" - "The full format includes all library dependencies (more secure).")) + "The full format includes all library dependencies (more secure).")) config_grid.Add(format_label, flag=wx.ALIGN_CENTER_VERTICAL) self.format = wx.Choice(panel, -1, choices=["Minimal", "Full"]) @@ -235,10 +252,11 @@ def InitUI(self): main_sizer.Add(button_sizer, flag=wx.ALL|wx.ALIGN_RIGHT, border=15) panel.SetSizer(main_sizer) - + ### Binds self.Bind(wx.EVT_BUTTON, self.OnOk, id=wx.ID_OK) self.Bind(wx.EVT_BUTTON, self.OnClose, id=wx.ID_CANCEL) + self.Bind(wx.EVT_BUTTON, self.OnShowStandaloneHelp, id=wx.ID_HELP) # NOUVEAU self.Bind(wx.EVT_CHECKBOX, self.OnChecked, id=self._cb1.GetId()) self.Bind(wx.EVT_CHOICE, self.OnChoice, id=self.kernel.GetId()) @@ -246,6 +264,156 @@ def InitUI(self): self._tc.SetFocus() self._tc.SetInsertionPointEnd() + + def OnShowStandaloneHelp(self, event): + """Show help dialog about standalone package generation""" + + help_msg = _( + "STANDALONE PACKAGE GENERATOR\n\n" + "═══════════════════════════════════════\n\n" + "OVERVIEW:\n\n" + "This tool creates a standalone, executable package of your DEVS model.\n" + "The generated package can run simulations WITHOUT DEVSimPy installed,\n" + "making it perfect for deployment, distribution, and production use.\n\n" + "═══════════════════════════════════════\n\n" + "PACKAGE CONFIGURATION:\n\n" + "• Filename: Name of the ZIP package to create\n" + " - Must end with .zip extension\n" + " - Example: MyModel-nogui-pkg.zip\n" + " - Default based on your model name\n\n" + "• Directory: Where to save the generated package\n" + " - Click 'Browse...' to select location\n" + " - Package will be created in this directory\n" + " - Ensure you have write permissions\n\n" + "• Format: Package complexity level\n" + " - Minimal: Only essential library files (smaller size)\n" + " • Faster to generate and transfer\n" + " • May miss some dependencies\n" + " • Good for simple models\n\n" + " - Full: All library dependencies included (larger size)\n" + " • More reliable and self-contained\n" + " • Works on any system\n" + " • Recommended for distribution\n\n" + "═══════════════════════════════════════\n\n" + "SIMULATION OPTIONS:\n\n" + "• Add Simulation Kernel:\n" + " - Includes DEVS simulation engine files\n" + " - Required if package will run simulations\n" + " - Enables kernel selection below\n" + " - Uncheck only for model-only packages\n\n" + "• Add Docker File:\n" + " - Includes Dockerfile for containerization\n" + " - Allows running in Docker containers\n" + " - Useful for cloud deployment\n" + " - Creates isolated, reproducible environment\n\n" + "• Add NTL Flag (Infinite Loop):\n" + " - NTL = No Time Limit\n" + " - Simulation runs until models are inactive\n" + " - Good for event-driven simulations\n" + " - Package will use -ntl command line flag\n\n" + "═══════════════════════════════════════\n\n" + "KERNEL CONFIGURATION:\n\n" + "(Only available when 'Add Simulation Kernel' is checked)\n\n" + "• Kernel: Choose DEVS simulation engine\n\n" + " - PyDEVS:\n" + " • Classic DEVS simulator\n" + " • Single-threaded execution\n" + " • Simple and reliable\n" + " • Good for small models\n\n" + " - PyPDEVS:\n" + " • Parallel DEVS simulator (recommended)\n" + " • Multi-threaded execution\n" + " • Better performance\n" + " • Supports real-time mode\n\n" + " - KafkaDEVS:\n" + " • Distributed DEVS over Apache Kafka\n" + " • For large-scale simulations\n" + " • Requires Kafka infrastructure\n" + " • Advanced use only\n\n" + "• Real Time Mode:\n" + " - Only available with PyPDEVS kernel\n" + " - Synchronizes simulation with wall clock\n" + " - Useful for hardware-in-the-loop\n" + " - Slower but matches real time\n\n" + "═══════════════════════════════════════\n\n" + "WHAT'S IN THE PACKAGE:\n\n" + "The generated ZIP file contains:\n\n" + "• Your DEVS model in YAML format\n" + "• All referenced Python model files\n" + "• Required library dependencies\n" + "• Simulation kernel (if selected)\n" + "• Dockerfile (if selected)\n" + "• README with usage instructions\n" + "• Command-line execution script\n\n" + "═══════════════════════════════════════\n\n" + "HOW TO USE THE PACKAGE:\n\n" + "After generation:\n\n" + "1. Extract the ZIP file on target system\n" + "2. Install Python 3.x if not present\n" + "3. Run the provided script:\n" + " python run_simulation.py\n\n" + "4. (Optional) Build Docker image:\n" + " docker build -t mymodel .\n" + " docker run mymodel\n\n" + "No DEVSimPy installation required!\n\n" + "═══════════════════════════════════════\n\n" + "USE CASES:\n\n" + "• Deployment: Send model to users without DEVSimPy\n" + "• Production: Run simulations on servers\n" + "• Cloud: Deploy in Docker containers\n" + "• Distribution: Share models with colleagues\n" + "• Archiving: Preserve complete model state\n" + "• Testing: Run models in isolated environments\n\n" + "═══════════════════════════════════════\n\n" + "REQUIREMENTS:\n\n" + "• Valid YAML model file\n" + "• All model Python files accessible\n" + "• Write permissions in target directory\n" + "• Sufficient disk space (varies by format)\n\n" + "═══════════════════════════════════════\n\n" + "TIPS:\n\n" + "- Use 'Full' format for maximum compatibility\n" + "- Include simulation kernel for executable packages\n" + "- Add Docker file for cloud deployment\n" + "- Test package on target system before distribution\n" + "- PyPDEVS is recommended for better performance\n" + "- Real-time mode useful for IoT/embedded systems\n" + "- Check README in generated package for details\n\n" + "═══════════════════════════════════════\n\n" + "TROUBLESHOOTING:\n\n" + "• Package generation fails:\n" + " → Check YAML file is valid\n" + " → Ensure all Python files are accessible\n" + " → Verify write permissions\n\n" + "• Package won't run:\n" + " → Use 'Full' format instead of 'Minimal'\n" + " → Check Python version compatibility\n" + " → Install missing dependencies\n\n" + "• Docker build fails:\n" + " → Ensure Docker is installed\n" + " → Check Dockerfile syntax\n" + " → Review Docker logs for errors" + ) + + try: + import wx.lib.dialogs + dlg = wx.lib.dialogs.ScrolledMessageDialog( + self, + help_msg, + _("Standalone Package Generator Help"), + size=(700, 650) + ) + dlg.ShowModal() + dlg.Destroy() + except Exception as e: + # Fallback + wx.MessageBox( + help_msg, + _("Standalone Package Generator Help"), + wx.OK | wx.ICON_INFORMATION + ) + + def OnChoice(self, event): selected_option = self.kernel.GetStringSelection() self._cb4.Enable(selected_option == 'PyPDEVS') diff --git a/devsimpy/WizardGUI.py b/devsimpy/WizardGUI.py index 8ab47fd3..35cc5364 100644 --- a/devsimpy/WizardGUI.py +++ b/devsimpy/WizardGUI.py @@ -389,7 +389,6 @@ def __init__(self, *args, **kwargs): page1.add_stuff(bt2) # page1.add_stuff(btgpt) - print(is_detached_framed, parent, parent.GetTopLevelParent()) ### if left click on the DetachedFrame, port instance can be created if is_detached_framed: bt3 = wx.RadioButton(page1, wx.NewIdRef(), _('Input Port')) diff --git a/devsimpy/plugins/state_trajectory.py b/devsimpy/plugins/state_trajectory.py index 92ffed33..b32bed85 100644 --- a/devsimpy/plugins/state_trajectory.py +++ b/devsimpy/plugins/state_trajectory.py @@ -437,13 +437,15 @@ def UnConfig(): global diagram main = wx.GetApp().GetTopWindow() - nb2 = main.GetDiagramNotebook() - currentPage = nb2.GetCurrentPage() - diagram = currentPage.diagram - lst = [a.label for a in [s for s in diagram.GetShapeList() if isinstance(s, CodeBlock)]] + if hasattr(main, 'GetDiagramNotebook'): + nb2 = main.GetDiagramNotebook() + currentPage = nb2.GetCurrentPage() + diagram = currentPage.diagram - for label in lst: - shape = diagram.GetShapeByLabel(label) - if hasattr(shape, 'state_trajectory'): - del shape.state_trajectory \ No newline at end of file + lst = [a.label for a in [s for s in diagram.GetShapeList() if isinstance(s, CodeBlock)]] + + for label in lst: + shape = diagram.GetShapeByLabel(label) + if hasattr(shape, 'state_trajectory'): + del shape.state_trajectory \ No newline at end of file