Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to http://semver.org/spec/v2.0.0.html[Semantic Versioni
== https://github.com/robotframework/RIDE[Unreleased]

=== Added
- Added Settings Editor button to Preferences dialog, to edit settings.cfg.
- Created backup of settings.cfg to allow recovering some settings when broken upgrades.
- Added current executing keyword and other statuses to TestRunner status bar.
- Added Config Panel button to supported installed Plugins next to their name in Plugin Manager dialog.
- Added Config Panel button to Plugins, working examples in Text Editor and Test Runner.
Expand Down
2 changes: 1 addition & 1 deletion README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Likewise, the current version of wxPython, is 4.2.2, but RIDE is known to work w

`pip install -U robotframework-ride`

(3.8 <= python <= 3.13) Install current development version (**2.2dev20**) with:
(3.8 <= python <= 3.13) Install current development version (**2.2dev21**) with:

`pip install -U https://github.com/robotframework/RIDE/archive/develop.zip`

Expand Down
4 changes: 4 additions & 0 deletions src/robotide/application/CHANGELOG.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Changelog</title><link rel="stylesheet" type="text/css" href="docbook-xsl.css" /><meta name="generator" content="DocBook XSL Stylesheets Vsnapshot" /></head><body><div xml:lang="en" class="article" lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="id1337"></a>Changelog</h2></div></div><hr /></div><p>All notable changes to this project will be documented in this file.</p><p>The format is based on <a class="ulink" href="http://keepachangelog.com/en/1.0.0/" target="_top">Keep a Changelog</a>
and this project adheres to <a class="ulink" href="http://semver.org/spec/v2.0.0.html" target="_top">Semantic Versioning</a>.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="_ulink_url_https_github_com_robotframework_ride_unreleased_ulink"></a>1. <a class="ulink" href="https://github.com/robotframework/RIDE" target="_top">Unreleased</a></h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a id="_added"></a>1.1. Added</h3></div></div></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">
Added Settings Editor button to Preferences dialog, to edit settings.cfg.
</li><li class="listitem">
Created backup of settings.cfg to allow recovering some settings when broken upgrades.
</li><li class="listitem">
Added current executing keyword and other statuses to TestRunner status bar.
</li><li class="listitem">
Added Config Panel button to supported installed Plugins next to their name in Plugin Manager dialog.
Expand Down
2 changes: 0 additions & 2 deletions src/robotide/application/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
from contextlib import contextmanager
from pathlib import Path

from pygments.styles.dracula import background

from ..namespace import Namespace
from ..controller import Project
from ..spec import librarydatabase
Expand Down
4 changes: 3 additions & 1 deletion src/robotide/application/releasenotes.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ def set_content(self, html_win, content):
</ul>
<p><strong>New Features and Fixes Highlights</strong></p>
<ul class="simple">
<li>Added Settings Editor button to Preferences dialog, to edit settings.cfg.</li>
<li>Created backup of settings.cfg to allow recovering some settings when broken upgrades.</li>
<li>Changed some informative dialogs and JSON Editor to use the customized colors.</li>
<li>Added current executing keyword and other statuses to TestRunner status bar.</li>
<li>Modified import statements to allow running RIDE without Robot Framework installed or versions older than 6.0.</li>
Expand Down Expand Up @@ -232,7 +234,7 @@ def set_content(self, html_win, content):
<pre class="literal-block">python -m robotide.postinstall -install</pre>
<p>or</p>
<pre class="literal-block">ride_postinstall.py -install</pre>
<p>RIDE {VERSION} was released on 12/April/2025.</p>
<p>RIDE {VERSION} was released on 14/April/2025.</p>
<!-- <br/>
<h3>May The Fourth Be With You!</h3>
<h3>Celebrate the bank holiday, 10th June, Day of Portugal, Portuguese Communities and Camões!!</h3>
Expand Down
6 changes: 6 additions & 0 deletions src/robotide/editor/customsourceeditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import wx.stc as stc
from robotide.editor.pythoneditor import PythonSTC
from wx import Colour
from ..widgets import ImageProvider

# ---------------------------------------------------------------------------
# This is how you pre-establish a file filter so that the dialog
Expand Down Expand Up @@ -534,6 +535,11 @@ def main(filepath, frame=None):
if frame is None:
frame = wx.Frame(None)
CodeEditorPanel(frame, None, filepath)
image_provider = ImageProvider()
frame.SetTitle(filepath)
frame.SetSize((800, 600))
frame.SetIcon(wx.Icon(image_provider.RIDE_ICON))
frame.CenterOnScreen()
frame.Show(True)
app.MainLoop()
# ----------------------------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions src/robotide/preferences/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def __init__(self, settings):
self.settings = settings
self._preference_panels = []
self._add_builtin_preferences()
self.settings_path = self.settings._default_path

@property
def preference_panels(self):
Expand Down
26 changes: 24 additions & 2 deletions src/robotide/preferences/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,14 @@
"""

import builtins
import os.path

import wx
from wx import Colour
from wx.lib.scrolledpanel import ScrolledPanel

from .settings import RideSettings
from ..widgets import ButtonWithHandler

_ = wx.GetTranslation # To keep linter/code analyser happy
builtins.__dict__['_'] = wx.GetTranslation
Expand Down Expand Up @@ -199,15 +202,25 @@ class PanelContainer(wx.Panel):
"""
def __init__(self, *args, **kwargs):
super(PanelContainer, self).__init__(*args, **kwargs)

self.parent = self.GetParent()
self._current_panel = None
self._settings = RideSettings()
self.settings = self._settings['General']
self.title = wx.StaticText(self, label="Your message here")
hsizer = wx.BoxSizer(wx.HORIZONTAL)
config_button = ButtonWithHandler(self, _('Settings'), bitmap='wrench_orange.png',
fsize=self.settings[FONT_SIZE],
handler=lambda e: self.on_edit_settings())
config_button.SetBackgroundColour(self.settings['background'])
config_button.SetOwnBackgroundColour(self.settings['background'])
config_button.SetForegroundColour(self.settings['foreground'])
hsizer.Add(config_button, 0, wx.TOP | wx.RIGHT | wx.EXPAND, 4)
hsizer.Add(self.title, 0, wx.TOP | wx.LEFT | wx.EXPAND, 4)
self.panels_container = ScrolledPanel(self, wx.ID_ANY, style=wx.TAB_TRAVERSAL)
self.panels_container.SetupScrolling()
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.title, 0, wx.TOP | wx.LEFT | wx.EXPAND, 4)
sizer.Add(hsizer)
# sizer.Add(self.title, 0, wx.TOP | wx.LEFT | wx.EXPAND, 4)
sizer.Add(wx.StaticLine(self), 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 4)
sizer.Add(self.panels_container, 1, wx.EXPAND)
self.SetSizer(sizer)
Expand Down Expand Up @@ -256,3 +269,12 @@ def ShowPanel(self, panel):
def SetTitle(self, title):
"""Set the title of the panel"""
self.title.SetLabel(title)

def on_edit_settings(self):
"""Starts Text Editor for settings file and closes all if changed"""
from ..editor import customsourceeditor
from ..context import SETTINGS_DIRECTORY
main_settings_path = os.path.join(SETTINGS_DIRECTORY, 'settings.cfg')
customsourceeditor.main(main_settings_path)
# DEBUG close parent test
# self.parent.Close()
1 change: 1 addition & 0 deletions src/robotide/preferences/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def __init__(self, settings, *args, **kwargs):
# don't have the time to do that right now, so this will have
# to suffice.

# print(f"DEBUG: preferences/general.py GeneralPeferences settings_path={self._settings._default_path}")
ui_language = self._select_ui_language()
font_editor = self._create_font_editor()
colors_sizer = self.create_colors_sizer()
Expand Down
22 changes: 13 additions & 9 deletions src/robotide/preferences/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ def _copy_or_migrate_user_settings(settings_dir, source_path, dest_file_name):
m_error = None
if not os.path.exists(source_path):
raise(FileNotFoundError(source_path))
else:
shutil.copyfile(source_path, source_path + '._backup')
if not dest_file_name:
dest_file_name = os.path.basename(source_path)
settings_path = os.path.join(settings_dir, dest_file_name)
Expand All @@ -60,12 +62,12 @@ def _copy_or_migrate_user_settings(settings_dir, source_path, dest_file_name):
SettingsMigrator(source_path, settings_path).migrate()
# print(f"DEBUG: settings After migration {source_path} with {settings_path}")
except ConfigObjError as parsing_error:
print("WARNING! corrupted configuration file replaced with defaults")
print("WARNING! corrupted configuration file replaced with defaults\n"
f"A backup of the settings is at: {source_path + '._backup'}")
print(parsing_error)
m_error = parsing_error
shutil.copyfile(settings_path, settings_path + '_old_broken')
print("(backed up corrupted configuration file at: %s)" %
(settings_path + '_old_broken'))
print(f"(backed up corrupted configuration file at: {settings_path + '_old_broken'}")
shutil.copyfile(source_path, settings_path)
# print("DEBUG: source %s corrupted settings %s\n" % (source_path, settings_path))
finally: # DEBUG Try to merge some settings
Expand Down Expand Up @@ -362,22 +364,24 @@ def __init__(self, path=None):
elif path.endswith('.cfg') and os.path.exists(path):
self._default_path = path
# print(f"DEBUG: settings.py RideSettings SETTINGS {self._default_path=}")
user_path = initialize_settings(self._default_path)
Settings.__init__(self, user_path)
self._settings_dir = os.path.dirname(user_path)
self.user_path = initialize_settings(self._default_path)
Settings.__init__(self, self.user_path)
self._settings_dir = os.path.dirname(self.user_path)
# print(f"DEBUG: RideSettings, self._settings_dir={self._settings_dir}")
self.get('install root', os.path.dirname(os.path.dirname(__file__)))
self.executable = self.get('executable', EXECUTABLE)
if self.executable != EXECUTABLE:
digest = 0
for c in EXECUTABLE:
digest += ord(c)
new_user_path = user_path.replace("settings.cfg", f"settings_{digest}.cfg")
new_user_path = initialize_settings(user_path, new_user_path)
new_user_path = self.user_path.replace("settings.cfg", f"settings_{digest}.cfg")
new_user_path = initialize_settings(self.user_path, new_user_path)
Settings.__init__(self, new_user_path)
self._settings_dir = os.path.dirname(new_user_path)
self.set('install root', os.path.dirname(os.path.dirname(__file__)))
self.executable = self.set('executable', EXECUTABLE)
self.set('executable', EXECUTABLE)
self.set('last_settings_path', new_user_path)
self.user_path = new_user_path

def get_path(self, *parts):
"""Returns path which combines settings directory and given parts."""
Expand Down
3 changes: 3 additions & 0 deletions src/robotide/ui/mainframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,9 +337,12 @@ def get_selected_datafile_controller(self):
return self.tree.get_selected_datafile_controller()

def on_close(self, event):
from ..preferences import RideSettings
if self._allowed_to_exit():
try:
perspective = self.aui_mgr.SavePerspective()
# Next restore of settings is because we may have edited from Preferences
self._application.settings = RideSettings(self._application.settings_path)
self._application.settings.set('AUI Perspective', perspective)
# deinitialize the frame manager
self.aui_mgr.UnInit()
Expand Down
2 changes: 1 addition & 1 deletion src/robotide/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
#
# Automatically generated by `tasks.py`.

VERSION = 'v2.2dev20'
VERSION = 'v2.2dev21'
2 changes: 1 addition & 1 deletion utest/resources/mocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def GetMenuItemCount(self):
class FakeSettings(Settings):
def __init__(self, settings=None):
self.fake_cfg = os.path.join(os.path.dirname(__file__), 'fake.cfg')

self._default_path = self.fake_cfg
# make sure fake cfg is clean
with open(self.fake_cfg, 'wb') as f:
f.write(_FAKE_CFG_CONTENT)
Expand Down
1 change: 1 addition & 0 deletions utest/settings/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ def tearDown(self):
self._remove_path(self.user_settings_path)
self._remove_path((self.user_settings_path+'_old_broken'))
self._remove_path(os.path.join(self.settings_dir, 'new_settings.cfg'))
self._remove_path(os.path.join(self.settings_dir, 'new_settings.cfg._backup'))
self._remove_path(os.path.join(self.settings_dir, 'my_settings.cfg'))
os.removedirs(self.settings_dir)

Expand Down
139 changes: 139 additions & 0 deletions utest/ui/test_managesettingsdialogs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Copyright 2008-2015 Nokia Networks
# Copyright 2016- Robot Framework Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import builtins
import os

import pytest
DISPLAY = os.getenv('DISPLAY')
if not DISPLAY:
pytest.skip("Skipped because of missing DISPLAY", allow_module_level=True) # Avoid failing unit tests in system without X11
import wx
import unittest
from robotide.preferences.managesettingsdialog import SaveLoadSettings
from time import sleep
from utest.resources import FakeSettings, UIUnitTestBase


_ = wx.GetTranslation # To keep linter/code analyser happy
builtins.__dict__['_'] = wx.GetTranslation

ID_LOAD = 5551
ID_SAVE = 5552
ID_CANCEL = -1
settings = FakeSettings()

class MyEvent(object):
def __init__(self, iid):
self.id = iid

def GetId(self):
return self.id

def Skip(self):
pass


class TestSaveLoadSettings(UIUnitTestBase):

def test_on_load_cancel(self):
self.frame = wx.Frame(None)
self.frame.CenterOnScreen()
self.frame.Show()
dlg = SaveLoadSettings(self.frame, settings)
# wx.CallLater(5000, dlg.EndModal,ID_CANCEL)
dlg.CenterOnParent()
dlg.Show()
sleep(5)
result = dlg.on_load(MyEvent(ID_SAVE))
assert dlg.GetTitle() == _("Save or Load Settings")
assert result==ID_CANCEL

def test_on_load(self):
self.frame = wx.Frame(None)
self.frame.CenterOnScreen()
self.frame.Show()
dlg = SaveLoadSettings(self.frame, settings)
wx.CallLater(5000, dlg.Close, True)
dlg.CenterOnParent()
# dlg.Show()
sleep(2)
# TODO: mock wx.FileDialog
# result = dlg.on_load(MyEvent(ID_LOAD))
assert dlg.GetTitle() == _("Save or Load Settings")
# assert result==ID_LOAD

def test_on_save_cancel(self):
self.frame = wx.Frame(None)
self.frame.CenterOnScreen()
self.frame.Show()
dlg = SaveLoadSettings(self.frame, settings)
# wx.CallLater(5000, dlg.EndModal,ID_CANCEL)
dlg.CenterOnParent()
dlg.Show()
sleep(5)
result = dlg.on_save(MyEvent(ID_LOAD))
assert dlg.GetTitle() == _("Save or Load Settings")
assert result==ID_CANCEL

def test_on_save(self):
self.frame = wx.Frame(None)
self.frame.CenterOnScreen()
self.frame.Show()
dlg = SaveLoadSettings(self.frame, settings)
wx.CallLater(5000, dlg.Close, True)
dlg.CenterOnParent()
# dlg.Show()
sleep(2)
# TODO: mock wx.FileDialog
# result = dlg.on_save(MyEvent(ID_SAVE)
assert dlg.GetTitle() == _("Save or Load Settings")
# assert result==ID_SAVE

def test_on_close(self):
self.frame = wx.Frame(None)
self.frame.CenterOnScreen()
self.frame.Show()
dlg = SaveLoadSettings(self.frame, settings)
# wx.CallLater(5000, dlg.EndModal,ID_CANCEL)
dlg.CenterOnParent()
dlg.Show()
sleep(5)
result = dlg.on_close()
assert dlg.GetTitle() == _("Save or Load Settings")
assert result==ID_CANCEL

def test_load_and_merge_fails(self):
self.frame = wx.Frame(None)
self.frame.CenterOnScreen()
self.frame.Show()
settings.add_section('Plugins')
settings.get_without_default('Plugins').add_section('Grid')
dlg = SaveLoadSettings(self.frame, settings)
# wx.CallLater(5000, dlg.EndModal,ID_CANCEL)
dlg.CenterOnParent()
dlg.Show()
sleep(2)
# TODO: assert Exception
try:
dlg.load_and_merge('nonvalid_path.cfg')
except KeyError:
pass




if __name__ == '__main__':
unittest.main()
Loading