Wing Tips: Extending Wing with Python (Part 2 of 4)

Jun 17, 2019


In this issue of Wing Tips we continue to look at how to extend Wing's functionality, by setting up a project that can be used to develop and debug extension scripts written for (and with) Wing, just as you would work with any other Python code.

If you haven't already read it, you may want to check out Part 1 of this series, where we introduced Wing's scripting framework and set up auto-completion for the scripting API.

Creating a Scripting Project

To debug extension scripts written for Wing, you will need to set up a new project that is configured so that Wing can debug itself. The manual steps for doing this are documented in Debugging Extension Scripts. However, let's use an extension script to do this automatically.

Copy the following Python code and paste it into a new file in Wing, using the File > New menu item:

import sys
import os
import collections
import wingapi

def new_scripting_project():
  """Create a new Wing project that is set up for developing and
  debugging scripts using the current instance of Wing"""

  app = wingapi.gApplication

  def setup_project():

    # Set the Python Executable to the Python that runs Wing
    proj = app.GetProject()
    proj.SetPythonExecutable(None, sys.executable)
    if not __debug__:
      from proj.attribs import kPyRunArgs
      app.fSingletons.fFileAttribMgr.SetValue(kPyRunArgs, None,
                                              ('custom', '-u -O'))

    # Add Wing's installation directory to the project
    proj.AddDirectory(app.GetWingHome())

    # Set main debug file to Wing's entry point
    wingpy = os.path.join(app.GetWingHome(), 'bootstrap', 'wing.py')
    proj.SetMainDebugFile(wingpy)

    # Setup the environment for auto-completion on the API and some additional
    # values needed only on macOS; use OrderedDict because later envs depend
    # on earlier ones
    env = collections.OrderedDict()
    env['PYTHONPATH'] = os.path.join(app.GetWingHome(), 'src')
    if sys.platform == 'darwin':
      runtime_dir = os.path.join(app.GetWingHome(), 'bin', '__os__', 'osx')
      files = os.listdir(runtime_dir)
      for fn in files:
        if fn.startswith('runtime-qt'):
          qt_ver = fn[len('runtime-'):]
          break
      env.update(collections.OrderedDict([
        ('RUNTIMES', runtime_dir),
        ('QTVERSION', qt_ver),
        ('QTRUNTIME', '${RUNTIMES}/runtime-${QTVERSION}'),
        ('SCIRUNTIME', '${RUNTIMES}/runtime-scintillaedit-${QTVERSION}'),
        ('DYLD_LIBRARY_PATH', '${QTRUNTIME}/lib:${SCIRUNTIME}/lib'),
        ('DYLD_FRAMEWORK_PATH', '${DYLD_LIBRARY_PATH}'),
      ]))
    app.GetProject().SetEnvironment(None, 'startup', env)

  # Create a new project; this prompts the user to save any unsaved files first
  # and only calls the completion callback if they don't cancel
  app.NewProject(setup_project)
new_scripting_project.contexts = [wingapi.kContextNewMenu('Scripts')]

Save this to a file named utils.py or any other name ending in .py in the scripts directory that is located inside Wing's Settings Directory, as listed in Wing's About box. Then select Reload All Scripts from the Edit menu to get Wing to discover the new script file. Once that's done, Wing monitors the file and will reload it automatically if you edit it and save changes to disk.

You can now create your project with New scripting project from the Scripting menu that should appear in the menu bar:

/images/blog/scripting-2/new-scripting-menu-item.png

This prompts to save any edited files before closing the current project, and then creates and configures the new project.

Debugging Extension Scripts

Now you're ready to develop and debug extension scripts with Wing. Try this now by appending the following to the script file that you created in the previous section:

import wingapi
def hello_world():
  wingapi.gApplication.ShowMessageDialog("Hello", "Hello world!")
hello_world.contexts = [wingapi.kContextNewMenu("Scripts")]

Save this to disk and then set a breakpoint on the third line, on the call to ShowMessageDialog.

Next, uncheck the Projects > Auto-reopen Last Project preference so that the debugged copy of Wing opens the default project and not your already-open scripting project:

/images/blog/scripting-2/no-auto-reopen.png

Then select Start/Continue from the Debug menu to start up a copy of Wing running under its own debugger. This copy of Wing should also contain a Scripts menu in its menu bar, with an item Hello world:

/images/blog/scripting-2/scripts-menu.png

Select that and you will reach the breakpoint you set in the outer instance of Wing (the one you started debug from):

/images/blog/scripting-2/breakpoint.png

You will see and can navigate the entire stack, but Wing will not be able to show source files for most of its internals. If you need to see the source code of Wing itself, you will have to obtain the source code as described in Advanced Scripting.



That's it for now! In the next Wing Tip we'll look look at how extension scripts can collect arguments from the user.



Share this article: