Robot Framework Integrated Development Environment (RIDE)
__main__.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # encoding=utf-8
3 # Copyright 2008-2015 Nokia Networks
4 # Copyright 2016- Robot Framework Foundation
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17 
18 
19 import sys
20 try:
21  import wx
22  from wx import Colour
23 except ImportError:
24  sys.stderr.write("No wxPython installation detected!"
25  "\n"
26  "Please ensure that you have wxPython installed "
27  "before running RIDE. "
28  "You can obtain wxPython from "
29  "https://wxpython.org/pages/downloads/\n"
30  "or pip install wxPython")
31  exit(-1)
32 
33 from os.path import exists, join
34 from robotide.widgets import RIDEDialog
35 
36 __doc__ = """
37 Usage: python ride_postinstall.py [options] <-install|-remove>
38  or python -m robotide.postinstall [options] <-install|-remove>
39  -install - Installs a Desktop Shortcut to RIDE.
40  -remove - [TODO] Removes a Desktop Shortcut to RIDE.
41  options: -q - Quiet, don't ask user for confirmation.
42  -f - Force action.
43  -help - This help.
44 """.strip()
45 # TODO: Add -remove, to remove desktop shortcut
46 
47 
49  try:
50  from wx import version
51  except ImportError:
52  sys.stderr.write("No wxPython installation detected!"
53  "\n"
54  "Please ensure that you have wxPython installed "
55  "before running RIDE. "
56  "You can obtain wxPython from "
57  "https://wxpython.org/pages/downloads/\n"
58  "or pip install wxPython")
59  return False
60  else:
61  sys.stderr.write("wxPython is installed.\n%s\n" % version())
62  return True
63 
64 
65 class MessageDialog(RIDEDialog):
66  def __init__(self, parent, message, title, ttl=10):
67  RIDEDialog.__init__(self, title=title, parent=parent, size=(300, 200))
68 
69  self.CenterOnScreen(wx.BOTH)
70  self.timeToLivetimeToLive = ttl
71 
72  st_msg = wx.StaticText(self, -1, message)
73  self.settimetolivemsgsettimetolivemsg = wx.StaticText(self, -1, 'Closing this dialog box in %ds...' % self.timeToLivetimeToLive)
74  vbox = wx.BoxSizer(wx.VERTICAL)
75  vbox.Add(st_msg, 0, wx.ALIGN_CENTER | wx.TOP, 40)
76  vbox.Add(self.settimetolivemsgsettimetolivemsg, 0, wx.ALIGN_CENTER | wx.TOP, 10)
77  self.SetSizer(vbox)
78  self.SetAffirmativeId(wx.ID_OK)
79  self._create_buttons_create_buttons()
80  self.SetBackgroundColour(Colour(self.color_background))
81  self.SetForegroundColour(Colour(self.color_foreground))
82  self.timertimer = wx.Timer(self)
83  self.timertimer.Start(1000) # Generate a timer event every second
84  self.Bind(wx.EVT_CLOSE, self.OnCloseOnClose)
85  self.Bind(wx.EVT_TIMER, self.onTimeronTimer, self.timertimer)
86  self.Bind(wx.EVT_BUTTON, self.OnCancelOnCancel, id=wx.ID_CANCEL)
87  self.Bind(wx.EVT_BUTTON, self.OnNoOnNo, id=wx.ID_NO)
88  self.Bind(wx.EVT_CHAR_HOOK, self.OnKeyPressedOnKeyPressed)
89 
90  def OnKeyPressed(self, event):
91  key_code = event.GetKeyCode()
92  if key_code == wx.WXK_ESCAPE:
93  self.EndModal(wx.ID_NO)
94  event.Skip()
95 
96  def OnCancel(self, evt):
97  self.EndModal(wx.ID_NO)
98 
99  def OnClose(self, evt):
100  self.EndModal(wx.ID_NO)
101 
102  def OnNo(self, evt):
103  self.EndModal(wx.ID_NO)
104 
105  def onTimer(self, evt):
106  self.timeToLivetimeToLive -= 1
107  self.settimetolivemsgsettimetolivemsg.SetLabel('Closing this dialog box in %ds...' % self.timeToLivetimeToLive)
108 
109  if self.timeToLivetimeToLive == 0:
110  self.timertimer.Stop()
111  self.EndModal(wx.ID_NO)
112 
113  def _create_buttons(self):
114  buttons = self.CreateStdDialogButtonSizer(wx.OK|wx.CANCEL)
115  self.SetBackgroundColour(Colour(self.color_background))
116  self.SetForegroundColour(Colour(self.color_foreground))
117  for item in self.GetChildren():
118  if isinstance(item, (wx.Button, wx.BitmapButton)):
119  item.SetBackgroundColour(Colour(self.color_secondary_background))
120  item.SetOwnBackgroundColour(Colour(self.color_secondary_background))
121  item.SetForegroundColour(Colour(self.color_secondary_foreground))
122  item.SetOwnForegroundColour(Colour(self.color_secondary_foreground))
123  self.Sizer.Add(buttons, flag=wx.ALIGN_CENTER | wx.ALL, border=5)
124 
125 
126 def _askyesno(title, message, frame=None):
127  if frame is None:
128 
131  _ = wx.App()
132  parent = wx.Frame(None, size=(0, 0))
133  else:
134  parent = wx.Frame(frame, size=(0, 0))
135  parent.CenterOnScreen()
136  dlg = MessageDialog(parent, message, title, ttl=8)
137  result = dlg.ShowModal() in [ wx.ID_YES, wx.ID_OK]
138  print("Result %s" % result)
139  if dlg:
140  dlg.Destroy()
141  parent.Destroy()
142  return result
143 
144 
145 def _askdirectory(title, initialdir, frame=None):
146  import wx
147  if frame is None:
148 
151  _ = wx.App()
152  parent = wx.Frame(None, size=(0, 0))
153  else:
154  parent = wx.Frame(frame, size=(0, 0))
155  parent.CenterOnScreen()
156  dlg = wx.DirDialog(parent, title, initialdir, style=wx.DD_DIR_MUST_EXIST)
157  if dlg.ShowModal() == wx.ID_OK:
158  result = dlg.GetPath()
159  else:
160  result = None
161  dlg.Destroy()
162  parent.Destroy()
163  return result
164 
165 
167  from os.path import expanduser
168  from os import environ, chmod, chown
169  import subprocess
170  import pwd
171  import sysconfig
172  DEFAULT_LANGUAGE = environ.get('LANG', '').split(':')
173  # TODO: Add more languages
174  desktop = {"de": "Desktop", "en": "Desktop", "es": "Escritorio",
175  "fi": r"Työpöytä", "fr": "Bureau", "it": "Scrivania",
176  "pt": r"Área de Trabalho"}
177  user = str(subprocess.check_output(['logname']).strip(), encoding='utf-8')
178  try:
179  ndesktop = desktop[DEFAULT_LANGUAGE[0][:2]]
180  directory = join("/home", user, ndesktop)
181  defaultdir = join("/home", user, "Desktop")
182  if not exists(directory):
183  if exists(defaultdir):
184  directory = defaultdir
185  else:
186  if not option_q:
187  directory = _askdirectory(title="Locate Desktop Directory",
188  initialdir=join(expanduser('~')),
189  frame=frame)
190  else:
191  directory = None
192  except KeyError:
193  if not option_q:
194  directory = _askdirectory(title="Locate Desktop Directory",
195  initialdir=join(expanduser('~')),
196  frame=frame)
197  else:
198  directory = None
199  if directory is None:
200  sys.stderr.write("Desktop shortcut creation aborted!\n")
201  return False
202  try:
203  link = join(directory, "RIDE.desktop")
204  except UnicodeError:
205  link = join(directory.encode('utf-8'), "RIDE.desktop")
206  if not exists(link) or option_f:
207  if not option_q and not option_f:
208  if not _askyesno("Setup", "Create desktop shortcut?", frame):
209  return False
210  roboticon = join(sysconfig.get_paths()["purelib"], "robotide", "widgets", "robot.ico")
211  if not exists(roboticon):
212  try:
213  import robotide as _
214  roboticon = join(_.__path__[0], "widgets", "robot.ico")
215  except ImportError:
216  pass
217  if not exists(roboticon):
218  roboticon = join("FIXME: find correct path to: .../site-packages/", "robotide", "widgets", "robot.ico")
219  with open(link, "w+") as shortcut:
220  shortcut.write(f"#!/usr/bin/env xdg-open\n[Desktop Entry]\n"
221  f"Exec={sys.executable} -m robotide.__init__\n"
222  f"Comment=A Robot Framework IDE\nGenericName=RIDE\n"
223  f"Icon={roboticon}\n"
224  f"Name=RIDE\nStartupNotify=true\nTerminal=false\n"
225  "Type=Application\nX-KDE-SubstituteUID=false\n")
226  uid = pwd.getpwnam(user).pw_uid
227  chown(link, uid, -1) # groupid == -1 means keep unchanged
228  chmod(link, 0o744)
229 
230 
232  import os
233  import shutil
234  import subprocess
235  ride_app_name = 'RIDE.app'
236  application_path = '/Applications'
237  ride_app_pc_path = os.path.join(application_path, ride_app_name)
238  ride_app_module_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), ride_app_name)
239 
240  if not exists(ride_app_pc_path) or option_f:
241  if not option_q and not option_f and not _askyesno("Setup", "Create application shortcut?", frame):
242  return False
243  app_script = os.path.join(ride_app_module_path, 'Contents', 'MacOS', 'RIDE')
244  with open(app_script, 'w+') as shortcut:
245  shortcut.write("#!/bin/sh\nPAL=$PATH\nfor i in `cat /etc/paths`\n do\n PAL=\"$PAL:$i\"\n"
246  " done\nPATH=$PAL\nexport $PATH\n{} -m robotide.__init__ $* 2>"
247  " /dev/null &\n".format(sys.executable))
248  if exists(ride_app_pc_path):
249  shutil.rmtree(ride_app_pc_path, True)
250  shutil.copytree(ride_app_module_path, ride_app_pc_path)
251  user = str(subprocess.check_output(['logname']).strip(), encoding='utf-8')
252  user_desktop_link = '/Users/' + user + '/Desktop/' + ride_app_name
253  if exists(user_desktop_link):
254  os.remove(user_desktop_link)
255  try:
256  os.symlink(ride_app_pc_path, user_desktop_link)
257  except Exception:
258  pass
259 
260 
262  # Dependency of http://sourceforge.net/projects/pywin32/
263  import os
264  import sys
265  try:
266  from win32com.shell import shell, shellcon
267  except ImportError:
268  sys.stderr.write("Cannot create desktop shortcut.\nPlease install"
269  " pywin32 from https://github.com/mhammond/pywin32\n"
270  "or pip install pywin32")
271  return False
272  desktop = shell.SHGetFolderPath(0, shellcon.CSIDL_DESKTOP, None, 0)
273  link = os.path.join(desktop, 'RIDE.lnk')
274  public_link = os.path.join(os.getenv('PUBLIC'), 'Desktop', 'RIDE.lnk')
275  icon = os.path.join(sys.prefix, 'Lib', 'site-packages', 'robotide',
276  'widgets', 'robot.ico')
277  if not (exists(public_link) or exists(link)) or option_f:
278  if not option_q and not option_f:
279  if not _askyesno("Setup", "Create desktop shortcut?", frame):
280  sys.stderr.write("Users can create a Desktop shortcut to RIDE "
281  "with:\n%s -m robotide.postinstall -install\n"
282  % sys.executable)
283  return False
284  import pythoncom
285  shortcut = pythoncom.CoCreateInstance(shell.CLSID_ShellLink, None,
286  pythoncom.CLSCTX_INPROC_SERVER,
287  shell.IID_IShellLink)
288  command_args = " -c \"from robotide import main; main()\""
289  shortcut.SetPath(sys.executable.replace('python.exe', 'pythonw.exe'))
290  shortcut.SetArguments(command_args)
291  shortcut.SetDescription("Robot Framework testdata editor")
292  shortcut.SetIconLocation(icon, 0)
293  persist_file = shortcut.QueryInterface(pythoncom.IID_IPersistFile)
294  from pywintypes import com_error
295  try:
296  persist_file.Save(public_link, 0)
297  sys.stderr.write(f"Desktop shortcut created for all users.")
298  except com_error:
299  persist_file.Save(link, 0)
300 
301 
302 def create_desktop_shortcut(platform, frame=None):
303  if platform.startswith("linux"):
304  return _create_desktop_shortcut_linux(frame)
305  elif platform.startswith("darwin"):
306  return _create_desktop_shortcut_mac(frame)
307  elif platform.startswith("win"):
309  else:
310  sys.stderr.write("Unknown platform {0}: Failed to create desktop short"
311  "cut.".format(platform))
312  return False
313 
314 
315 def caller(frame, platform):
316  # Options
317  global option_q
318  global option_f
319  option_q = None
320  option_f = frame is not None
321  # We don't verify install because called from RIDE
322  return create_desktop_shortcut(platform, frame)
323 
324 
325 def main(args):
326  # Options
327  global option_q
328  global option_f
329  option_q = option_f = None
330  option_q = next((x for x in args if x == "-q"), None)
331  if option_q is not None:
332  del args[args.index(option_q)]
333  option_q = True
334  option_f = next((x for x in args if x == "-f"), None)
335  if option_f is not None:
336  del args[args.index(option_f)]
337  option_f = True
338  arg = args[-1] if len(args) == 1 and args[-1] in ['-install', '-remove',
339  '-help'] else None
340  if arg == '-install':
341  doit = True
342  platform = sys.platform.lower()
343  doit = verify_install()
344  if doit:
345  create_desktop_shortcut(platform)
346  elif arg == '-remove':
347  sys.stderr.write("Sorry, -remove is not implemented yet.\n")
348  else:
349  sys.stderr.write(__doc__)
350  sys.stderr.write("\n")
351 
352 
353 if __name__ == '__main__':
354  main(sys.argv[1:])
def __init__(self, parent, message, title, ttl=10)
Definition: __main__.py:66
def _create_desktop_shortcut_windows(frame=None)
Definition: __main__.py:261
def caller(frame, platform)
Definition: __main__.py:315
def _create_desktop_shortcut_mac(frame=None)
Definition: __main__.py:231
def _askdirectory(title, initialdir, frame=None)
Definition: __main__.py:145
def create_desktop_shortcut(platform, frame=None)
Definition: __main__.py:302
def _askyesno(title, message, frame=None)
Definition: __main__.py:126
def _create_desktop_shortcut_linux(frame=None)
Definition: __main__.py:166