Robot Framework Integrated Development Environment (RIDE)
application.py
Go to the documentation of this file.
1 # Copyright 2008-2015 Nokia Networks
2 # Copyright 2016- Robot Framework Foundation
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 
16 import locale
17 import os
18 import wx
19 locale.setlocale(locale.LC_ALL, 'C')
20 
21 from contextlib import contextmanager
22 
23 from ..namespace import Namespace
24 from ..controller import Project
25 from ..spec import librarydatabase
26 from ..ui import LoadProgressObserver
27 from ..ui.mainframe import RideFrame
28 from .. import publish
29 from .. import context, contrib
30 from ..context import coreplugins
31 from ..preferences import Preferences, RideSettings
32 from ..application.pluginloader import PluginLoader
33 from ..application.editorprovider import EditorProvider
34 from ..application.releasenotes import ReleaseNotes
35 from ..application.updatenotifier import UpdateNotifierController, UpdateDialog
36 from ..ui.mainframe import ToolBar
37 from ..ui.treeplugin import TreePlugin
38 from ..ui.fileexplorerplugin import FileExplorerPlugin
39 from ..utils import RideFSWatcherHandler, run_python_command
40 from ..lib.robot.utils.encodingsniffer import get_system_encoding
41 from ..publish import PUBLISHER
42 from ..publish.messages import RideSettingsChanged
43 from ..preferences.settings import _Section
44 from wx import Colour
45 from ..widgets.button import ButtonWithHandler
46 
47 
49  def __init__(self):
50  BaseException.__init__(self, 'HELP! I have no clue how to theme this.')
51 
52 
53 class RIDE(wx.App):
54 
55  def __init__(self, path=None, updatecheck=True):
56  self._updatecheck_updatecheck = updatecheck
57  self.workspace_pathworkspace_path = path
58  context.APP = self
59  wx.App.__init__(self, redirect=False)
60 
61  def OnInit(self):
62  # DEBUG To test RTL
63  # self._initial_locale = wx.Locale(wx.LANGUAGE_ARABIC)
64  self._initial_locale_initial_locale = wx.Locale(wx.LANGUAGE_ENGLISH_US)
65  # Needed for SetToolTipString to work
66  wx.HelpProvider.Set(wx.SimpleHelpProvider()) # TODO adjust to wx versions
67  self.settingssettings = RideSettings()
68  librarydatabase.initialize_database()
69  self.preferencespreferences = Preferences(self.settingssettings)
70  self.namespacenamespace = Namespace(self.settingssettings)
71  self._controller_controller = Project(self.namespacenamespace, self.settingssettings)
72  self.frameframe = RideFrame(self, self._controller_controller)
73 
74  self._editor_provider_editor_provider = EditorProvider()
75  self._plugin_loader_plugin_loader = PluginLoader(self, self._get_plugin_dirs_get_plugin_dirs(),
76  coreplugins.get_core_plugins())
77  self._plugin_loader_plugin_loader.enable_plugins()
78  perspective = self.settingssettings.get('AUI Perspective', None)
79  if perspective:
80  self.frameframe._mgr.LoadPerspective(perspective, True)
81  try:
82  nb_perspective = self.settingssettings.get('AUI NB Perspective', None)
83  if nb_perspective:
84  self.frameframe.notebook.LoadPerspective(nb_perspective)
85  except Exception as e:
86  print(f"RIDE: There was a problem loading panels position."
87  f" Please delete the definition 'AUI NB Perspective' in "
88  f"{os.path.join(context.SETTINGS_DIRECTORY, 'settings.cfg')}")
89  if not isinstance(e, IndexError): # If is with all notebooks disabled, continue
90  raise e
91  self.treeplugintreeplugin = TreePlugin(self)
92  if self.treeplugintreeplugin.settings['_enabled']:
93  self.treeplugintreeplugin.register_frame(self.frameframe)
94  self.fileexplorerpluginfileexplorerplugin = FileExplorerPlugin(self, self._controller_controller)
95  if self.fileexplorerpluginfileexplorerplugin.settings['_enabled']:
96  self.fileexplorerpluginfileexplorerplugin.register_frame(self.frameframe)
97  if not self.treeplugintreeplugin.opened:
98  self.treeplugintreeplugin.close_tree()
99  # else:
100  # wx.CallLater(200, self.treeplugin.populate, self.model)
101  if not self.fileexplorerpluginfileexplorerplugin.opened:
102  self.fileexplorerpluginfileexplorerplugin.close_tree()
103  self.editoreditor = self._get_editor_get_editor()
104  self._load_data_load_data()
105  self.treeplugintreeplugin.populate(self.modelmodelmodel)
106  self.treeplugintreeplugin.set_editor(self.editoreditor)
107  self._find_robot_installation_find_robot_installation()
108  self._publish_system_info_publish_system_info()
109  self.frameframe.Show()
110  self.frameframe._mgr.Update()
111  wx.CallLater(200, ReleaseNotes(self).bring_to_front)
112  wx.CallLater(200, self.fileexplorerpluginfileexplorerplugin._update_tree)
113  if self._updatecheck_updatecheck:
114  wx.CallAfter(UpdateNotifierController(self.settingssettings).notify_update_if_needed, UpdateDialog)
115  self.Bind(wx.EVT_ACTIVATE_APP, self.OnAppActivateOnAppActivate)
116  PUBLISHER.subscribe(self.SetGlobalColourSetGlobalColour, RideSettingsChanged)
117  return True
118 
119  def _ApplyThemeToWidget(self, widget,
120  foreColor=wx.BLUE, backColor=wx.LIGHT_GREY, theme={}):
121  background = theme['background']
122  foreground = theme['foreground']
123  secondary_background = theme['secondary background']
124  secondary_foreground = theme['secondary foreground']
125  background_help = theme['background help']
126  foreground_text = theme['foreground text']
127  # font_size = theme['font size']
128  # font_face = theme['font face']
129  if isinstance(widget, wx.lib.agw.aui.auibar.AuiToolBar) or isinstance(widget, ToolBar):
130  auiDefaultToolBarArt = wx.lib.agw.aui.AuiDefaultToolBarArt()
131  auiDefaultToolBarArt.SetDefaultColours(wx.GREEN)
132  widget.SetBackgroundColour(background)
133  widget.SetOwnBackgroundColour(background)
134  widget.SetForegroundColour(foreground)
135  widget.SetOwnForegroundColour(foreground)
136  """
137  widget.SetBackgroundColour(Colour(200, 222, 40))
138  widget.SetOwnBackgroundColour(Colour(200, 222, 40))
139  widget.SetForegroundColour(Colour(7, 0, 70))
140  widget.SetOwnForegroundColour(Colour(7, 0, 70))
141  """
142  # or
143  elif isinstance(widget, wx.Control):
144  if not isinstance(widget, (wx.Button, wx.BitmapButton, ButtonWithHandler)):
145  widget.SetForegroundColour(foreground) # or foreColor
146  widget.SetBackgroundColour(background) # or backColor
147  widget.SetOwnBackgroundColour(background)
148  widget.SetOwnForegroundColour(foreground)
149  else:
150  widget.SetForegroundColour(secondary_foreground)
151  widget.SetBackgroundColour(secondary_background)
152  widget.SetOwnBackgroundColour(secondary_background)
153  widget.SetOwnForegroundColour(secondary_foreground)
154  elif isinstance(widget, (wx.TextCtrl, wx.lib.agw.aui.auibook.TabFrame, wx.lib.agw.aui.auibook.AuiTabCtrl)):
155  widget.SetForegroundColour(foreground_text) # or foreColor
156  widget.SetBackgroundColour(background_help) # or backColor
157  elif isinstance(widget, (RideFrame, wx.Panel)):
158  widget.SetForegroundColour(foreground) # or foreColor
159  widget.SetBackgroundColour(background) # or foreColor
160  elif isinstance(widget, wx.MenuItem):
161  widget.SetTextColour(foreground)
162  widget.SetBackgroundColour(background)
163  # print(f"DEBUG: Application ApplyTheme wx.MenuItem {type(widget)}")
164  else:
165  widget.SetBackgroundColour(background)
166  widget.SetOwnBackgroundColour(background)
167  widget.SetForegroundColour(foreground)
168  widget.SetOwnForegroundColour(foreground)
169 
172 
173  def _WalkWidgets(self, widget, indent=0, indentLevel=4, theme={}):
174 
175  widget.Freeze()
176  # print(f"DEBUG Application General : _WalkWidgets background {theme['background']}")
177  self._ApplyThemeToWidget_ApplyThemeToWidget(widget=widget, theme=theme)
178  for child in widget.GetChildren():
179  if (not child.IsTopLevel()): # or isinstance(child, wx.PopupWindow)):
180  indent += indentLevel
181  self._WalkWidgets_WalkWidgets(child, indent, indentLevel, theme)
182  indent -= indentLevel
183  widget.Thaw()
184 
185  def SetGlobalColour(self, message):
186  if message.keys[0] != "General":
187  return
188  # print(f"DEBUG Application General : Enter SetGlobalColour message= {message.keys[0]}")
189  app = wx.App.Get()
190 
193  _root = app.GetTopWindow()
194  theme = self.settingssettings.get('General', None)
195  font_size = theme['font size']
196  font_face = theme['font face']
197  font = _root.GetFont()
198  font.SetFaceName(font_face)
199  font.SetPointSize(font_size)
200  _root.SetFont(font)
201  self._WalkWidgets_WalkWidgets(_root, theme=theme)
202  # print(f"DEBUG Application General : SetGlobalColour AppliedWidgets check Filexplorer and Tree")
203  if theme['apply to panels'] and self.fileexplorerpluginfileexplorerplugin.settings['_enabled']:
204  self.fileexplorerpluginfileexplorerplugin.settings['background'] = theme['background']
205  self.fileexplorerpluginfileexplorerplugin.settings['foreground'] = theme['foreground']
206  self.fileexplorerpluginfileexplorerplugin.settings['foreground text'] = theme['foreground text']
207  self.fileexplorerpluginfileexplorerplugin.settings['background help'] = theme['background help']
208  self.fileexplorerpluginfileexplorerplugin.settings['font size'] = theme['font size']
209  self.fileexplorerpluginfileexplorerplugin.settings['font face'] = theme['font face']
210  if self.fileexplorerpluginfileexplorerplugin.settings['opened']:
211  self.fileexplorerpluginfileexplorerplugin.OnShowFileExplorer(None)
212  if theme['apply to panels'] and self.treeplugintreeplugin.settings['_enabled']:
213  self.treeplugintreeplugin.settings['background'] = theme['background']
214  self.treeplugintreeplugin.settings['foreground'] = theme['foreground']
215  self.treeplugintreeplugin.settings['foreground text'] = theme['foreground text']
216  self.treeplugintreeplugin.settings['background help'] = theme['background help']
217  self.treeplugintreeplugin.settings['font size'] = theme['font size']
218  self.treeplugintreeplugin.settings['font face'] = theme['font face']
219  if self.treeplugintreeplugin.settings['opened']:
220  self.treeplugintreeplugin.OnShowTree(None)
221  """
222  all_windows = list()
223  general = self.settings.get('General', None)
224  # print(f"DEBUG: Application General {general['background']} Type message {type(message)}")
225  # print(f"DEBUG: Application General message keys {message.keys} old {message.old} new {message.new}")
226  background = general['background']
227  foreground = general['foreground']
228  background_help = general['background help']
229  foreground_text = general['foreground text']
230  font_size = general['font size']
231  font_face = general['font face']
232  font = _root.GetFont()
233  font.SetFaceName(font_face)
234  font.SetPointSize(font_size)
235  _root.SetFont(font)
236 
237  def _iterate_all_windows(root):
238  if hasattr(root, 'GetChildren'):
239  children = root.GetChildren()
240  if children:
241  for c in children:
242  _iterate_all_windows(c)
243  all_windows.append(root)
244 
245  _iterate_all_windows(_root)
246 
247  for w in all_windows:
248  if hasattr(w, 'SetHTMLBackgroundColour'):
249  w.SetHTMLBackgroundColour(wx.Colour(background_help))
250  w.SetForegroundColour(wx.Colour(foreground_text)) # 7, 0, 70))
251  elif hasattr(w, 'SetBackgroundColour'):
252  w.SetBackgroundColour(wx.Colour(background)) # 44, 134, 179))
253 
254  # if hasattr(w, 'SetOwnBackgroundColour'):
255  # w.SetOwnBackgroundColour(wx.Colour(background)) # 44, 134, 179))
256 
257  if hasattr(w, 'SetForegroundColour'):
258  w.SetForegroundColour(wx.Colour(foreground)) # 7, 0, 70))
259 
260  # if hasattr(w, 'SetOwnForegroundColour'):
261  # w.SetOwnForegroundColour(wx.Colour(foreground)) # 7, 0, 70))
262 
263  if hasattr(w, 'SetFont'):
264  w.SetFont(font)
265  """
266 
268  publish.RideLogMessage(context.SYSTEM_INFO).publish()
269 
270  @property
271  model = property
272 
273  def model(self):
274  return self._controller_controller
275 
276  def _get_plugin_dirs(self):
277  return [self.settingssettings.get_path('plugins'),
278  os.path.join(self.settingssettings['install root'], 'site-plugins'),
279  contrib.CONTRIB_PATH]
280 
281  def _get_editor(self):
282  from ..editor import EditorPlugin
283  from ..editor.texteditor import TextEditorPlugin
284  for pl in self._plugin_loader_plugin_loader.plugins:
285  maybe_editor = pl._plugin
286  if (isinstance(maybe_editor, EditorPlugin) or
287  isinstance(maybe_editor, TextEditorPlugin)) and \
288  maybe_editor.__getattr__("_enabled"):
289  return maybe_editor
290 
291  def _load_data(self):
292  self.workspace_pathworkspace_path = self.workspace_pathworkspace_path or self._get_latest_path_get_latest_path()
293  if self.workspace_pathworkspace_path:
294  self._controller_controller.update_default_dir(self.workspace_pathworkspace_path)
295  observer = LoadProgressObserver(self.frameframe)
296  self._controller_controller.load_data(self.workspace_pathworkspace_path, observer)
297 
299  output = run_python_command(
300  ['import robot; print(robot.__file__ + \", \" + robot.__version__)'])
301  robot_found = b"ModuleNotFoundError" not in output and output
302  if robot_found:
303  system_encoding = get_system_encoding()
304  rf_file, rf_version = output.strip().split(b", ")
305  publish.RideLogMessage("Found Robot Framework version %s from %s." % (
306  str(rf_version, system_encoding), str(os.path.dirname(rf_file), system_encoding))).publish()
307  return rf_version
308  else:
310  publish.get_html_message('no_robot'), notify_user=True
311  ).publish()
312 
313  def _get_latest_path(self):
314  recent = self._get_recentfiles_plugin_get_recentfiles_plugin()
315  if not recent or not recent.recent_files:
316  return None
317  return recent.recent_files[0]
318 
320  from ..recentfiles import RecentFilesPlugin
321  for pl in self.get_pluginsget_plugins():
322  if isinstance(pl._plugin, RecentFilesPlugin):
323  return pl._plugin
324 
325  def get_plugins(self):
326  return self._plugin_loader_plugin_loader.plugins
327 
328 
329  def register_preference_panel(self, panel_class):
330  self.preferencespreferences.add(panel_class)
331 
332 
333  def unregister_preference_panel(self, panel_class):
334  self.preferencespreferences.remove(panel_class)
335 
336  def register_editor(self, object_class, editor_class, activate):
337  self._editor_provider_editor_provider.register_editor(object_class, editor_class,
338  activate)
339 
340  def unregister_editor(self, object_class, editor_class):
341  self._editor_provider_editor_provider.unregister_editor(object_class, editor_class)
342 
343  def activate_editor(self, object_class, editor_class):
344  self._editor_provider_editor_provider.set_active_editor(object_class, editor_class)
345 
346  def get_editors(self, object_class):
347  return self._editor_provider_editor_provider.get_editors(object_class)
348 
349  def get_editor(self, object_class):
350  return self._editor_provider_editor_provider.get_editor(object_class)
351 
352  @contextmanager
353  def active_event_loop(self):
354  # With wxPython 2.9.1, ProgressBar.Pulse breaks if there's no active
355  # event loop.
356  # See http://code.google.com/p/robotframework-ride/issues/detail?id=798
357  loop = wx.EventLoop()
358  wx.EventLoop.SetActive(loop)
359  yield
360  del loop
361 
362  def OnEventLoopEnter(self, loop):
363  if loop and wx.EventLoopBase.IsMain(loop):
364  RideFSWatcherHandler.create_fs_watcher(self.workspace_pathworkspace_path)
365 
366  def OnAppActivate(self, event):
367  if self.workspace_pathworkspace_path is not None and RideFSWatcherHandler.is_watcher_created():
368  if event.GetActive():
369  # print(f"DEBUG: OnAppActivate event.GetActive is_project_changed_from_disk = {self._controller.is_project_changed_from_disk()}")
370  #print(f"DEBUG: OnAppActivate event.GetActive is_workspace_dirty = {RideFSWatcherHandler.is_workspace_dirty()}")
371  #DEBUG if self._controller.is_project_changed_from_disk() or \
372  if RideFSWatcherHandler.is_workspace_dirty():
373  self.frameframe.show_confirm_reload_dlg(event)
374  RideFSWatcherHandler.stop_listening()
375  else:
376  RideFSWatcherHandler.start_listening(self.workspace_pathworkspace_path)
377  event.Skip()
def register_editor(self, object_class, editor_class, activate)
Definition: application.py:336
def unregister_editor(self, object_class, editor_class)
Definition: application.py:340
def get_editors(self, object_class)
Definition: application.py:346
def activate_editor(self, object_class, editor_class)
Definition: application.py:343
def _ApplyThemeToWidget(self, widget, foreColor=wx.BLUE, backColor=wx.LIGHT_GREY, theme={})
Definition: application.py:120
def _WalkWidgets(self, widget, indent=0, indentLevel=4, theme={})
Definition: application.py:173
def get_editor(self, object_class)
Definition: application.py:349
def __init__(self, path=None, updatecheck=True)
Definition: application.py:55
_editor_provider
DEBUG self.frame.Show()
Definition: application.py:74
def register_preference_panel(self, panel_class)
Add the given panel class to the list of known preference panels.
Definition: application.py:329
def unregister_preference_panel(self, panel_class)
Remove the given panel class from the known preference panels.
Definition: application.py:333
Shows release notes of the current version.
Definition: releasenotes.py:34
This class represents a general purpose log message.
Definition: messages.py:96
Provides a tree view for Files and Folders.
Provides a tree view for Test Suites.
Definition: treeplugin.py:66
def get_path(name, basedir)
Definition: xmlreaders.py:96
def run_python_command(command, mode='c')
Definition: __init__.py:107