From BlenderWiki

Jump to: navigation, search

Creating and debugging Python scripts in Blender with SPE and Winpdb

Witold Jaworski October 2007

Edited/Input by Jeffrey Blank January 2008


Introduction:

To begin with, in this tutorial I will not dig too deeply into many of SPE's feature that are not connected directly to Blender. This would take too long.

All information presented here is based on my own experience. For sure, I am not the most skillful user of either Blender or SPE. If you find that I have made a mistake in any place, feel free to correct it.

Required Software:

The following software is required to run SPE within Blender and needs to be installed in the order shown.

Standard Python:

The version of Python used to create this tutorial was 2.5.1. The version of Python that should be installed is dependent on the version and build of Blender you are running. The standard build of Blender 2.45 for Windows looks for Python 2.5. Windows systems do not have Python pre-installed. For details of a typical Windows install, see Appendix A.1.

Mac Os X and most Linux distributions ship with a standard Python installation by default. If this version is different from the one required by Blender, you will need to install the version that Blender requires. (For example to install Python 2.5 on Ubuntu: type "sudo apt-get install python2.5" at a terminal.

wxPython:

The version of wxPython used to create this tutorial was 2.8.6 (for Python 2.5). Installation packages of wxPython for all operating systems may be downloaded from wxPython.org. Appendix A.2 shows a typical wxPython installation for Windows.

SPE IDE:

The version of SPE used to create this tutorial was 0.8.4b (this version must have a build date after 23rd October 2007) The installation files should be available from pythonide.stani.be. The required version is only available by svn access at this time. SPE needs to be installed at C:\Program Files\Python\Lib\site-packages\_spe\ for Windows and /usr/lib/python2.5/site-packages for Linux. I will refer to this directory as site-packages for the rest of the tutorial.

Blender:

I have not mentioned Blender, version 2.42 or higher, because you probably already have it installed!

First Steps in SPE

After you have finished installing the software, you can run SPE from the Blender for the first time. Open the site-packages\_spe\spe.blend in Blender. The Blender screen should look like figure 2.1.

Figure 2.1 - Opening screen for spe.blend

You will see Blender's Text Editor window in the bottom panel. The editor will contain the SPE startup script. To run SPE, press AltP or select the Run Python Script command from File menu. This will open the SPE editor window as shown in figure 2.2

Figure 2.2 - Opening screen for SPE

SPE is really rich Python editor written by Stani (pythonide.stani.be). You can search for fragments of its description dispersed on the Web or buy its manual for which the small cost is a donation to further development of this program. (Look at the smiling face on Donate tab - isn't it encouraging to do this?).

Figure 2.3 - SPE console showing import command
Figure 2.4 - SPE console showing code completion
Figure 2.5 - SPE console showing variable descriptions.
SPE is a Python script that can be run from within Blender. This means that you have "live" access to Blender objects through the Shell window at the bottom of the SPE screen (see figure 2.3).

To make the SPE shell recognize the Blender module and it's children, I have typed "import Blender" as the first statement. You can now see that SPE uses syntax coloring in the shell window. The SPE shell now recognizes Blender functions and procedures and is attempting to help finish the commands that I am typing. In addition to commands, the SPE Shell also attempts to help me by suggesting object attributes (figure 2.4) and function descriptions (figure 2.5).

This interactive shell lets you play with live Blender objects. It gives you a quick way to try out ideas and concepts you may have while writing a script. There is also a special Blender tab, which lets you explore the Blender API.(figure 2.6). It wouldn't hurt to take a quick look through this tab. Who knows, maybe you'll discover commands you didn't know even existed!





Figure 2.6 - The 3 sections of the Blender API browser utility in SPE. (1 - module selector; 2 - module content explorer; 3 - details of selected element.)

There is also a Blender menu available when running SPE from within Blender (figure 2.7). (The menu is not visible when SPE is being run as a standalone application.) The first section of the Blender menu contains two commands that will be described in detail later on in this tutorial:

  • Load into Blender: loads current script into Blender as a Text datablock that can be changed in Text Editor window;
  • Reference in Blender: appends current script to Blender Scripts menu in Scripts Window.
Figure 2.7 - Blender pulldown menu in spe.
The next section in Blender menu contains only one command - Redraw Blender window. In fact, SPE forces this window to be redrawn at a given time interval. This interval can be set in the preferences dialog, found in the edit pulldown menu. The default interval is 1 second. This command is useful when you have specified a very long interval.

The third and fourth section of Blender menu contain links to related documentation sites.

The fifth section contains only one command: Add SPE and Windpdb to Blender menu. It adds two shortcuts (one for SPE, the second for Winpdb, the debugger) to Scripts → System menu in the Blender Scripts Window. (It is a kind of installation command which you usually have to run only once).

Figure 2.8 - SPE preferences located in the Edit pulldown menu.
The Redraw Blender window command exhibits the shortcomings of SPE. While SPE is open, you can switch to the Blender window and can see its contents but you cannot issue any commands. Neither the Blender user interface the mouse, or the keyboard can be used. This is because a wxPython application, which is what SPE is, is a child process of Blender. The wxPython child process takes over the main loop of the parent application and "consumes" all of messages and events posted to it. There is no simple solution for this effect. Because of this limitation we will use SPE as a "sidekick" editor for more advanced editor jobs, and the Blender Text Editor window for petty corrections. Switching between them is quick and easy, as I will show you.

Now, if we are going to use two different editors for the same Python script, we should synchronize their tab settings. The Python interpreter is very sensitive to this! When you type a ⇆ Tab in the Blender Text Editor, it puts a tab character into script text. It is not converted automatically into spaces. You have to issue a special command from Format menu to make that happen. It would be an annoying task to convert tabs to spaces over and over again. So, let's use tabs for spacing in both editors. It is good idea to set them at the beginning. To do this, select the Preferences command from Edit menu. (figure 2.8)

Once the Preferences dialog window is open, go to the Editor tab (figure 2.9) and click on the Use tabs checkbox and make sure that the Tab width is set to 4 (or whatever you have tabs set to in the Blender Text Window). You can also check the Refresh Time Interval setting for redrawing the Blender window under the General tab as shown if figure 2.10.

That's all we will do this first time in SPE. Now, when you close SPE, you will be back in Blender, and it will start to react on your commands again.

Figure 2.9 - SPE preferences panel, editor tab. Set tab settings in spe to match the tab settings in Blender's Text Editor.
Figure 2.10 - SPE preferences panel, General tab. Showing the refresh interval for blender.

Some Blender Features Related to Python

Before we start to write a Python script in SPE, I would like to present some of the features in Blender that we will use in this tutorial. Of course, there are also other utilities related to Python - it is not reasonable to describe all of them here.

Adding SPE and Winpdb Commands to the Blender Scripts Menu

Figure 3.1 - Blender drop-down menu in SPE.
There is a quicker and easier way to call SPE, that is to append its call as a command to the Blender Scripts menu. This will make it easily available while editing in any Blender file, not only the spe.blend file. To do this, just click the Add SPE and Winpdb to Blender command in the Blender drop down menu. (figure 3.1)

What did this command do? It has copied from site-packages\_spe\ the following files:

  • winpdb_blender.py (implements the Blender menu Scripts → System command Attach WinPdb debugger)
  • spe_blender.py (implements the Blender menu Scripts → System command Stani's Python Editor)

and placed them in either the Blender\.blender\scripts\ directory or the user script directory. I think that the user script directory is a better place for your Python files. This way, they won't be affected by Blender upgrades. You can set the User script directory in Blender's User Preferences Window in the File Paths section. See figure 3.2.

Figure 3.2 - User script path location in Blender's User Preference Menu.

If you change this setting after you ran the Add SPE and Winpdb command, you may want to rerun the command to copy the two scripts to your new script directory. This will also let SPE know where you are storing your scripts.

Figure 3.3 - Update Menus command in Blenders Scripts drop-down menu.
After the shortcut files are copied, you can close SPE and open the Scripts Window in Blender. To see the scripts you need to restart Blender or force the menu update with the Update Menus command in the Scripts menu (figure 3.3).

After running this command, you will find two new commands in the System submenu, Attach Winpdb debugger and Stani Python Editor. Later in this tutorial you will learn how to add your own commands to the Blender script menu. It is very easy, especially in SPE.

Missing Scripts on Blender Upgrade

Your scripts can be compiled "on the first use" by the Python compiler to the *.pyc files. If you upgrade Blender to a version that uses a newer version of Python (and you upgrade your Python and wxPython files as well!), let's say from Python 2.5 to Python 2.6, your scripts will disappear from the menu! Fortunately, you can easily resolve this problem by removing all of the *.pyc and *.pyo files from your scripts directory. This will force Python to compile them again.

Browsing Online Documentation of the Script Commands

Figure 3.4 - Scripts Help Browser in the Blenders Help drop-down menu.
You can find many scripts in Blender's Scripts menu. Its submenus reflect the content of "Scripts" submenu found in other Blender windows. For example, the contents of Scripts → Object submenu from Scripts Window is also available in the 3D View window, at Object → Scripts submenu. If you keep the mouse cursor over a script menu item for a while, a tooltip will appear. There is also more documentation, every script comes with its short manual! OK, I know that most of the programmers prefer "follow their instinct" while learning new software. I know I am in the minority, but, I prefer to read and find out what I should do, before I start. Anyway, it is good idea to look into documentation, that way, when something doesn't seem to not work as expected, you have somewhere to look for an answer.

To access this additional documentation, switch to the Blender User Preferences window, and from the Help menu select Scripts Help Browser command (figure 3.4). You should see a dialog box similar to figure 3.5 in an open Scripts Window. From this menu, you can select a program by pressing the Blender menu buttons. Each of these buttons matches a submenu from Scripts menu. (figure 3.6)

When you select an item, a detailed description of the selected script will be shown in the window as in figure 3.7. If you press the source button, the content of the script will be loaded as a Blender Text item into the current Blender file as shown in figure 3.8. This item bears the name of the script file. You can look at it by opening Text Editor window, presented in the next section.

Figure 3.5 - Scripts Help Browser menu.
Figure 3.7 -spe documentation accessed from the Scripts Help Browser.
Figure 3.6 - System menu in the Scripts Help Browser.
Figure 3.8 - Script source loaded into Blender Text Editor confirmation dialog.

Text Editor Window

Figure 3.10 - Selecting the loaded script into the Text Editor.
The Blender Text Editor window allows you to edit text files inside Blender. I would not call it "a simple text editor", because it supports syntax coloring for Python files, line numbering, and some other features. Let's call it "a moderate text editor", OK?
Figure 3.11 - Line Numbering and Syntax Coloring toggle buttons in Blenders Text Editor.
When you open the Text Editor, you will see an empty window. Where is the script that we loaded as a text element in the previous step of this tutorial? The Text editor has a dropdown list that allows you to switch between text elements that exists in the Blender file. These text elements can be anything, not only Python scripts, and can include comments, license terms, to dos, etc. Let's select the one containing the spe_blender.py script. (figure 3.10)

Now that we have a script loaded, let's turn on the Python specific features, line numbers and color syntax. Press the corresponding toggle buttons, as shown in figure 3.11.

When you want to run this script press AltP or select the File → Run Python Script command. When you are going to debug a script inside Blender - running it from this window is the only possible choice. Otherwise the Winpdb debugger, due to an internal error in Blender, will not be able to show the source lines.


Making a Script (or How to Make SPE and Blender Play Well Together)

In this section of the tutorial we will create a script that implements an operation that is missing in Blender. In Blender, it is possible to align a view to a selected object, but the view is always aligned to XY plane of selected object. (If you want to try select the cube and go to object mode, View → Align View → Align View to Selected). When modeling moderately complicated mechanical assemblies, like the landing gear of an airplane, I often want to align the view to any plane of an object. (i.e. ZX, YZ, ...) It is currently not possible to perform this operation. So... let's create a script for aligning the view that:

  • will be registered in Blender's Object → Scripts menu as Align View to Selected command. (View menu would be more appropriate, but it has no Scripts item, so you cannot add your script there);
  • will display a popup menu, where the user will be able to select the object plane (ZX, YZ, ...);

Let's start. Open Blender. For developing this script, we will use the default model, a single cube.

Switch the upper User Preferences panel into Scripts Window and select the Scripts → System → Stani's Python Editor command (remember we added it earlier in this tutorial?)

SPE may take a few seconds to open the first time it is called in a Blender session but all subsequent calls will load much quicker. (usually less than a second!) After the SPE window appears, we can type the beginning of the script as shown in figure 4.1.

Figure 4.1 - SPE editor with the beginning of our new script.
Figure 4.2 - Loading the new script into blender.
As you can see in the picture, you should repeat the script import statements in the SPE shell so that the code completion works properly. You can do it quickly by selecting the import statements in the script code, and selecting Execute in Shell command from the Editor's context menu or by pressing ⇧ ShiftCtrlE.

We start the writing script code with a crude version of the user interface, not touching the main task at all. Believe me, this way will allow you to create the script quicker, rather than coding the "internals" first and then creating the user interface at the end. (This is the way most beginning script writers do it.) After all you are the first user of your script, so make it easy on yourself! In fact, the user interface provides the means for the user to enter data and interact with your script. If the data that is input is wrong, even the best code in the script "internals" won't be able to help.

Figure 4.3 - The SPE status bar showing the script has been loaded into blender.
Figure 4.4 - Loading the new script into Blender's Text editor.
So, the first version of the script should deal with user input. First it checks if there is an object selected. If not, it displays a warning message to the user. Notice that a kind of simple popup menu is used as the message box. This is a common practice in Blender scripts. The string passed to the Draw.PupMenu() function is divided into two parts by a "|" character. The first part is the header and it has to contain the "%t" for title. The second part is a "menu item", the message in this case.

Now, let's save this script with the name "align_view.py". (SPE automatically checks the script for syntax errors). Then it is time to run it. Because there may be some errors in the code, we will load this script into Blender Text editor, and run it from there. To load it into Blender, select Blender → Load into Blender command (Figure 4.2) or press CtrlB. The result of this operation is shown on the SPE status bar.

Now we will close SPE and select the 'align_view.py' datablock in Blender. Switch the lower Blender panel into a Text Editor window. Remember, you always have to select the script data block from dropdown menu of the list items as shown in figure 4.4. (Blender never automatically shows the newly created/reloaded text item).

So here we have the script in a Blender Text editor window. Turn the line numbers and color syntax toggles on by pressing the buttons in the header bar. (figure 4.5)

Figure 4.5 - The script in Blenders text editor with line numbers and syntax coloring.

Next, use File → Run or AltP to execute the Python Script, and as we see in figure 4.6...

Figure 4.6 - An error message when running the script.

... we have an error! When you click the message, the error line will be highlighted in a pink color as shown in figure 4.7 (for the default Blender theme).

Figure 4.7 - Location of the error highlighted in the Text Editor.

OK, let's do what Blender told us to do, look at the Blender console to see what is wrong with this highlighted line. (figure 4.8)

Figure 4.8 - Blender console showing the cause of the error.

It seems that I typed the function name too fast and didn't use the SPE code completion feature. It should be GetSelected(), not getSelected(). Let's correct this error in the Blender Text Editor and save it with File → Save, or AltS. The first time you save the file, Blender asks you to confirm that you want to save over the existing file, as shown in figure 4.9. Because we have saved this file, SPE will open the updated version the next time we run it.

Figure 4.9 - Save Over confirmation in Blender's Text Editor.

OK, let's run our script again to see what happens (AltP)... No error message from Blender? Nice, so probably everything is working properly when we have one selected object in Blender scene. Now let's try the case in which nothing is selected. Go into the 3D View Window and press the A key (or the Select/Deselect All command from Object menu) to deselect the cube. All the objects in current scene should be deselected at this point. Lets run the script again (figure 4.10)...

Figure 4.10 - The script runs as expected with no objects selected.

Great! The script works as we want.

So now, let's go back to SPE (Scripts → System → Stani's Python Editor). As you can see in figure 4.11, we have added to the script to create a pop-up menu for data entry.

Figure 4.11 - SPE editor with new code for our script.

To make the code cleaner, the string that defines the popup menu has been set to a global MENU variable. My intention is for it to be a constant, so I marked this area as a "constants" section. The text after the "|" in the MENU string defines a single menu item. The expression "%x<digits>" is evaluated to an integer and returned by function PupMenu(). If the option selected from menu exists (that is, the user has not cancelled the operation), we will obtain the required user input. At this point the "core" function (ChangeViewTo) will be called and it will change the current view. On this phase of the script development, ChangeViewTo() does nothing, it just prints a diagnostics message to the console to let us know that it was called.

Let's load the revised script into Blender and see how it works. Save the script (CtrlS), send it to Blender (CtrlB) where it will replace the previous version of the align_view.py datablock), and close SPE (AltF4). Notice, that the align_view.py text was not initially selected in Blender Text Editor. As noted earlier, this is normal when updating the script from SPE. Now we load the script into the Text Editor by selecting align_view.py from the drop-down list. You should see something similar to figure 4.12.

Figure 4.12 - New text loaded into Blender's Text Editor.

After the script is loaded, select the cube in the 3D View window (since we deselected it in the previous step) and switch back to the Text editor and run the code with AltP. You should see the pop-up menu as shown in figure 4.13.

Figure 4.13 - The new pop-up menu for aligning the selected object.

We see the menu in figure 4.9. Let's run it few times, looking at the diagnostics messages on the console (printed by ChangeViewTo(), figure 4.14). OK, ChangeViewTo() is called each time.

Figure 4.14 - Blender console window showing output from the Align View script.

Now another test. Run the script again but do not select ANY item from "Object plane:" menu, press Esc instead. This will simulate a user who decided that they wanted to cancel this operation. In case of user cancellation the ChangeViewTo() should not be called, there is a conditional clause in line 23 for this case (see the script source in SPE in figure 4.11).

After cancelling out of the command, look at the console (figure 4.15). There should not be any new message from ChangeViewTo(), but.... THERE IS!?

Figure 4.15 - Blender console window showing output from the Align View script with the unexpected extra line.

Next, we have to debug the script to determine why ChangeViewTo() was called!

Script Debugging - Using Winpdb With Blender

Figure 5.1 - Starting Winpdb From Blenders Script Menu.
Debugging a script embedded in another application always requires a kind of interprocess communication. That's why we have to "attach" the Python debugger to Blender. Once you have attached it, you can keep Winpdb attached as long, as you need it. It will not disturb you in changing scripts or calling SPE, so its "attachment" is not a problem for the rest of environment.

Attaching Winpdb to Blender

From the Scripts Window select the Scripts → System → Attach Winpdb dDebugger command as in figure 5.1.(remember that we added it earlier from within SPE.)

After a while (a few seconds), the Winpdb window will open and you will see a screen similar to figure 5.2. The warning shown in the picture will always appear when you attach Winpdb to Blender. Click Ok to continue. You will then get a second warning as shown in figure 5.3. This is also normal. Click Ok to continue. I have tried to press Cancel to ignore this warning in the future as directed by the dialog box but the warning has always appeared.



Figure 5.2 - Opening Screen for Winpdb with an error.
Figure 5.3 - Second error dialog box.

Winpdb, An Introduction

Winpdb was written by Nir Aides as a platform independent GPL Python debugger. It supports multiple threads, namespace modification, embedded debugging, encrypted communication and is up to 20 times faster than pdb. (Taken directly from the Winpdb website.) The documentation is available at Winpdb website, easily accessible through the Help → Online docs command. There are also links to external tutorials. As with our discussions of SPE, I will only concentrate on using Winpdb with Blender in this tutorial. (If you want to support the Winpdb development you can go here to donate.

Figure 5.4 - Winpdb screen layout.

Controlling Winpdb

Figure 5.5 - Gui controls for Winpdb.
Figure 5.6 - Drop-down menu controls for Winpdb.
As in most software, there are three ways to control Winpdb while it is debugging a script. First is with the drop-down menu, second is with the keyboard and third is with the icons shown in the toolbar. Below is a description of each control. See figures 5.1 and 5.2 for the location of the controls.
  1. BREAK - Stop the current debugging session.
  2. GO - Starts the debugger and waits for a script to debug.
  3. NEXT - Step to the next line in the script.
  4. STEP INTO - Step into the selected function.
  5. RUN TO LINE -
  6. RETURN -

The Winpdb screen is divided into five panels as shown in figure 5.4. There are three panels (Namespace, Threads and Stack) on the left and two panels (Source and Console) on the right. For debugging this single-threaded Python script, I used the following panels:

  1. Source: shows the source, associated breakpoints, and executed line. Its functionality is similar to other debuggers;
  2. Console: shows some messages from Winpdb (in the picture you can see the sequence of messages from attaching Winpdb to Blender). It also shows results of commands typed in the Command line located below under the console window.
  3. Namespace panel: allows to check values of all local and global variables;
  4. Threads Panel: Since this script uses only a single thread I did not use this panel.
  5. Stack panel: shows current stack of Python function calls;

You can find a detailed description of Winpdb screen in a tutorial written by Chris Lasher.

Getting back to our script, we have now attached Winpdb to Blender but the source for the script we just ran did not appear in the source window! This is normal. It is specific to Blender (and may be an old bug, I don't know) that you are able to see only the source shown in Text Editor window. Since the script for this command was called from the Script menu, Winpdb can only see its stack and the line number of currently executed line. Anyway, we do not need to see it since we want to debug our script, not the menu command! So, in the Winpdb window click Go push button (or Control → Go, or press F5), to finish the menu command and unfreeze Blender. The state of Winpdb has been changed from "WAITING AT BREAK POINT" to "RUNNING" (The current status is shown at the bottom of Winpdb window, compare it with the state from figure 5.2). The cursor sometimes can be shown as a hourglass but Winpdb is still responsive. It will break into your code when you issue the Control → Break (F4) command while you are running a Python script in Blender.

Debugging Our Script

In Winpdb window issue the Break command if you haven't already done so. (You can also issue this command by pressing F4 or Control → Break from the menu). It will freeze Winpdb until you run a script in Blender.

Switch to Blender, and run the script from Text Editor window (press AltP or select File → Run Python Script command)

And here we are! Winpdb has caught the first line of our script as shown in figure 5.7. Let's issue the Next command (F6). After issuing the Next command once, the highlighted line has not been changed but its status changed from "C" to "L" as shown in figure 5.8. After pressing another F6, Winpdb starts to move through the script, line by line.

Figure 5.7 - Winpdb has caught the first line of our script.
Figure 5.8 - Line status changed from C to L.

When it reaches line 27 (the call to main()), use Control → Step Into (F7), instead of F6 (Next). You will enter the main() function. Place the cursor on line 23 and select Breakpoints Toggle → Breakpoint (or press F9) to tell Winpdb to stop there (it's better to skip over a function that calls the user interface)(figure 5.9) Then press F5 (or Control → Go) to let Blender to show the popup menu.

You will have to switch to Blender window and it may happen that you will not have a chance to select anything from the popup menu of your script. Blender's popup menus are very volatile. It seems that switching to the Blender window is enough for them to assume that the user has canceled the operation. Lucky for us, we want to test exactly this situation, cancellation of the menu. So, with your manual selection or without, you will reach the Winpdb breakpoint at line 23 as shown in figure 5.10.

Figure 5.9 - Stepping into the main() function at line 27.
Figure 5.10 - The breakpoint at line 23 is highlighted with red.
Figure 5.11 - Winpdb at the breakpoint.

When you look at the values of the variables in the Locals window pane in figure 5.11, you will see the reason that the ChangeViewTo() function was called again. When I created the conditional if statement at line 23, I assumed that Blender.Draw.PupMenu() returns None when user cancels the command. (This is the normal response from most other Python functions). Here I was wrong, and had not read the Blender API reference carefully enough. When nothing is selected or the action is cancelled, PupMenu() returns a value of -1! We have to change the test expression in the statement at line 23.

Figure 5.12 - Entering the eval command in the console pane.
Figure 5.13 - Viewing the output of the eval command in the console pane.

To test if the rest of the algorithm is OK, let's change value of the option variable in Winpdb. We will use the command line to do this. First, I will show you how to evaluate any Python expression in Winpdb. Since this debugger has no special Watch window, you have to use the "evaluate" command quite often. To begin with, type any expression in the Command line as shown in figure 5.12. When you press ↵ Enter, it will be evaluated in the Console as shown in figure 5.13. Rather than typing "eval" each time, you can use the shortcut: "v" as we do in figure 5.14).

Figure 5.14 - Getting the value of option using the 'v' shortcut for eval.
Figure 5.15 - Running the exec command to change the value of option to None.

To change value of a variable, use the "exec" command as in figure 5.15. When you press ↵ Enter, you will see the result in the Namespace → Locals tab. (figure 5.16) The shortcut for "exec" command is "x".

I didn't change the value of the option local variable just to show you how to do it, I also had another goal, to show you how Winpdb marks the return from a function. Now you press F7 (Step Into) just to check if the script will enter the ChangeViewTo() function at line 23. Notice that the current line has been moved to the last line of the procedure, and its status is not "L", but "R" like in figure 5.17

Figure 5.16 - Checking the value of option in both the Console and the Namespace panels.
Figure 5.17 - Line status changed to R.

At first glance it may seem strange that the script did not execute line 25 after line 23, but the answer for this mystery is the status of the debugee, on the left side, "R" means "Return". The script is just leaving the whole function, using the implicit return statement. So everything is in its proper place.

Press F5 (Go) to let Blender finish the script.

The source remains on the screen, even though Winpdb is no longer debugging our script. The state of Winpdb is "RUNNING". In this way, we have come back to the initial state of the environment.

You can now modify the code as needed. If the change is small you can quickly do it in Blender's Text Editor and save the updated version. If you have to redesign something, open SPE and make the changes to the script because it will be much more easier to do it there. Winpdb will patiently wait in the background until you need it again When you do need to run winpdb again, repeat the steps starting at the beginning of this section. (set Winpdb into Break mode by pressing F4, ...)

Detaching Winpdb from Blender

Figure 5.18 - Selecting detach from the File menu.
Figure 5.19 - Viewing the output of the Detach command in the console.

Usually, you will not need to do this manually. You should always close Blender first and Winpdb second. Otherwise, if you close Winpdb before Blender, Blender will crash at shutdown. If, for any reason, you want to close Winpdb before Blender, you have to issue a special Detach command. To detach Winpdb manually from Blender, break into any running script first and then issue the Detach command. (Winpdb needs to have a Python engine in active state to detach, that is the trick). So:

  • set Winpdb into break mode (as described in previous paragraph);
  • run any Python script (I often used Help → Tutorials command from User Preferences window for this purpose)

When Winpdb stops at the beginning of the script, issue the File → Detach command in Winpdb as shown in figure 5.18. Successful completion of this operation is shown on Winpdb console. (figure 5.19) After this, you can safely close Winpdb and the subsequent Blender shutdown should happen without any problem.

Adding the Script to Blender's Menu

Using SPE and Winpdb I have finished this script. The goal of this tutorial is to show how SPE and Winpdb can be used as a kind of IDE for Python in Blender, so I will not dig into details of changing the 3D view matrices. You can find the finished script at the end in Appendix D. Figure 6.1 also shows the rest of the new code to make this script work.

Figure 6.1 - Remainder of the Align to View script.
Figure 6.2 - Reference in Blender in the Blender drop-down menu.
Now, what can we do to allow quicker access to a script that will be used often? Let's place it in Blender's menus! There is special command to perform this action in SPE. Use the Blender → Reference in the Blender dropdown as shown in figure 6.2. (or press CtrlAltB)

If our script was saved in a directory other than the Blender script directories for Python commands:

  • Blender\.blender\scripts\
  • user script directory

it will be saved to one of these locations (these directories were already discussed here). First, SPE will try to save it to the user scripts directory, if it exists. If not, the script will be saved to Blender\.blender\scripts\. A message will appear in the status bar. The status bar informs us that the file was saved in new directory and Blender's signature was added. What is Blender's signature? It is a special beginning of the file, required for Blender menu commands. Figure 6.3 shows a typical Blender signature. You can find a detailed description of the Blender signature format in the Blender Python API Reference in the section titled "Registering scripts".

Figure 6.3 - Typical Blender signature block added by SPE.

While creating this signature, SPE tries to automatically determine some of the details, like your user name and current version of Blender. All you have to do is to change this content as needed to match your script and save the changes. Let's start with lines 1 through 8. They are required by Blender, and you should modify the text in apostrophes only:

  • Name: name to be shown in Blender's menus;
  • Group: name of the submenu in the Scripts menu in Scripts Window. You are only allowed to use the existing names. This script will be placed there as <Name>;
  • Blender: the minimal Blender version, - numerical and without a dot (e.g. 245 for version 2.45);
  • Tooltip: a one sentence description of the command;

Figure 6.4 shows what I used for the align view script we created in the previous sections.

Warning: I have encountered a problem with the optional #Submenu line that is described in the Blender Python API Reference. It seems that Blender does not behave as described and just ignores such lines.

I have also modified the documentation info (from line 11 to the end of the signature). It is used by the Scripts Help browser. (figure 6.4) OK, Now that we have updated all of the signature elements, let's save the file (CtrlS). Then close SPE (AltF4), and switch back to Blender.

Figure 6.4 - Final Blender signature block for the Align to View script.

In Blender, from Scripts Window, issue the Scripts → Update Menus command (figure 6.5). You will then find this script listed in Scripts → Object → Align view to selected. It is also placed in the 3D View window, as Object → Scripts → Align view to selected (figure 6.6) from which it is easily accessible.

Figure 6.5 - Location of Update Menus and Align to view in the scripts menu.
Figure 6.6 - Location of Align to View in the Objects → Scripts menu in the 3D window.
What about its documentation? Let's run Help → Scripts Help Browser from the User Preferences window. From the script groups, select Object → Align view to selected. The documentation screen will appear as shown in figure 6.7. Personally, I always create this documentation for my script commands. After a year, you can find that you remember nothing about tips and quirks for the script. Such documentation may not just help others, but may also help remind you!
Figure 6.7 - Align to View documentation in the Script Help Browser.

Appendix A. Detailed installation instructions:

In appendix A I will describe the install process in Windows for Python, wxPython and SPE. I only describe the install process in Windows since this is the operating system in which I work. You can get more detailed install instruction for any operating system supported by these programs at their respective websites.

Appendix A.1 - Python installation on Windows

Check the Blender console to see which which embedded Python version that it was compiled with. (It is printed when Blender starts) Now download the install program for the version of Python that Blender is using from http://www.python.org Figures A.1 through A.5 take you through a typical Python install on Windows.

Figure A.1 - Python install startup screen.
Figure A.2 - Enter the location for Python to install to.
Figure A.3 - Progress of Python install.
Figure A.4 - Press Finish to complete the installation.
Figure A.5 - Press YES to reboot Windows.

As shown in figure A.2, I have removed the version number from the default name for the install directory. (The default install directory for Python v2.5 is C:\Python25). This is not a required step, just my personal preference. It was better in for tutorial to refer to a Python folder with no particular version number in its name otherwise it might create the false impression that you NEED Python 2.5 (which is not the case.) You could just as easily use Python v2.3 or 2.4 at this time, depending on the current requirements for wxPython 2.8

Appendix A.2 - wxPython installation

Now we will continue with the installation of wxPython. Go to http://wxPython.download.php and download the version of wxPython that corresponds with the version of Python that you just installed or was previously installed on your machine.

Figures A.6 through A.10 take you through a typical wxPython install on Windows.

Figure A.6 - First screen in the wxPython install.
Figure A.7 - GPL license acceptance. Select accept and press NEXT to continue.
Figure A.8 - Enter directory to install wxPython to (Default is good).
Figure A.9 - Select components to be installed (Default should be fine).
Figure A.10 - Press Finish to complete the installation.

Appendix A.3 - SPE installation

Version 0.8.4.d of SPE has been released and can be found at Stani's website. To install, download the file to your hard drive and extract using your favorite archiving tool to the site-packages/_spe folder in Python. (C:\Python\Lib\site-packages\_spe in windows).

Alternatively, you can also down load the most up to date, bleeding edge version from svn. (Please note that the svn version may not be as stable as the released version and you should use at your own risk!) I will describe the process of obtaining SPE from the SVN repository using rapidsvn. You can use any of the available SVN tools available. There is a short tutorial on using Tortoise SVNlocated here

I have used RapidSVN. Its install file can be downloaded from http://www.rapidsvn.org. I will skip the process of installing this tool - nothing special, just keep clicking Next until the end.

Then, to get the latest SPE SVN, open RapidSVN and select the Export command as in figure A.11. A dialog box will appear. Fill it as shown in figure A.12 (the _spe directory should not exist yet!). Then click the OK button to continue. After a while you will get a message showing successful completion of installation. (figure A.13) Of course, you can add manually the SPE shortcut to the Windows Start menu. Point it to the _spe\SPE.pyw file.

Figure A.11 - Export command in RapidSVN.
Figure A.12 - Enter the information into URL and Destination Directory as shown. Note that the _spe directory under Destination Directory does not yet exist.
Figure A.13 - Message signaling completion of SPE install.

Appendix B. Content of the Command Scripts

The scripts that are used to run SPE and Winpdb from within Blender, spe_blender.py and winpdb_blender.py, were tested in a Windows environment. If they do not work on Linux, feel free to fix them. It would be also nice to let us know about the problems and the eventual solution. To make such modifications easier, I have discussed their content here.

Appendix B.1 "Stani's Python Editor" Command: spe_blender.py File

After removing all the historical comments and documentation strings, the core of this script is very simple:

import sys
if sys.modules.has_key('_spe.SPE'):
  reload(sys.modules.get('_spe.SPE'))
else: 
  import _spe.SPE

This script simply forces all of the startup procedures in SPE.py file to run again, and then loaded for a second time. This is ensured by calling reload() function. The expression in the conditional statement just checks if SPE has been already loaded. After SPE is called for the first time, there is no SP module listed among the loaded modules therefore we call "import _spe.SPE" to load it. In all other cases this module should be reloaded, not imported

Appendix B.2 "Attach Winpdb debugger" Command: winpdb_blender.py File

This script, after removing all documentation strings is more complicated than spe_blender.py. It is because it performs more complicated action. Debugging of embedded script via Winpdb is implemented as a client-server communication. The server of debugging information is located inside the Blender embedded Python. It is brought to "life" by calling special function from rpdb2 module (it is a part of Winpdb). The client of debugging session is the Winpdb application, run as separate process, aside Blender. The Winpdb client process has to be attached to the server in Blender. To instruct Winpdb that, after opening, it has to attach immediately the Blender, some command line switches are used:

import os
import sys
import subprocess
 
#
# helper parameters - usually you do not need to change them:
#
WAIT_TIMEOUT = 15 #time that this script will wait for attaching a Winpdb 
PASSWORD = 'blender' #Winpdb requires a password between the client and 
                          #the server, so this is the one we will use.
 
#
# We will try to load rpdb2 from implicit path. It may work for a standalone 
# Winpdb installation. This way this script may work with newer version of 
# Winpdb, than this one that is shipped with SPE:
#
try:#looking for the default/standalone winpdb installation:
    import rpdb2
except ImportError: #we will try localize it within SPE directories:
    import _spe.plugins.spe_winpdb #import this, to use winpdb packed with SPE
 
from rpdb2 import start_embedded_debugger, __file__ as rpdb2_file
 
def debug(what):
    #
    # We will form the argument list for Winpdb invocation:
    # 
 
    #
    # Argument 1: Python executable name. 
    # (We cannot utilize here os.executable, because it returns "Blender.exe", 
    # instead "python.exe)"
    #
    if os.name in ("nt","mac"): args=["pythonw"] # On Windows and Mac use this
    else: args = ["python"] #On Linux and other systems - standard python
    #
    # Argument 2: full path to Winpdb.py script name 
    #
    args.append(os.path.join(os.path.dirname(rpdb2_file),"winpdb.py"))
    #
    # Argument 3: -a(ttach) specified script
    #
    args.append("-a")
    #
    # Argument 4: -p(assword): it is only available on nt systems.
    #
    if os.name == "nt": 
        args.append("-p" + PASSWORD)
    #
    # Argument 5: name of the debugged script
    #
    args.append(what)
    #
    # Finally: run WinPdb...
    #
    if os.name == "nt":
        pid = subprocess.Popen(args) 
 
    else:
        #
        # On Linux systems: we have to pass the password with stdin!
        #
        pid = subprocess.Popen(args, stdin = subprocess.PIPE, stdout = subprocess.PIPE)
        pid.stdin.write(PASSWORD + "\n")
        pid.stdin.flush()
    #
    # ....and let it to connect, waiting for <timeout> seconds:
    #
    start_embedded_debugger(PASSWORD, timeout = WAIT_TIMEOUT)
 
#
# Every script in Blender is represented by the same name: <string>
#
debug("<string>")

In this script there is the debug() function. It performs the main action: builds the argument list, and calls Winpdb (via subprocess.Popen object). Its last operation is to call the rpdb2.start_embedded_debugger() method. This method freeze whole Blender, waiting for the Winpdb to be attached. That's why it has to be the last line executed by this script. The code outside debug() function tries to import rpdb2 module (it is the server side of Winpdb debugger). It tries to find a standalone installation of the Winpdb, first (the "try" section). (This shortcut should work also with standalone Winpdb installation). If it does not succeed, the script executes the alternative - tries to find the Winpdb that is supplied with SPE. To be imported successfully, rpdb2 requires an implicit path to its files. It will not allow you to import it as _spe.plugins.winpdb.rpdb2. The call to import _spe.plugins.spe_winpdb internally calls import rpdb2 from inside SPE directories. At the end the debug() function is called. Its argument is "<string>", because every Python script in Blender run not from Text Editor window is represented by such name.

Appendix B.3 The Script Used in This Tutorial

Here is the code of the final version of the align_view.py script used in this tutorial. You can copy and paste fragments of its code, if you do not want to type them manually:

#!BPY
 
#"""
#Name: 'Align view to selected'
#Blender: 245
#Group: 'Object'
#Tooltip: 'Align view to selected object'
#"""
 
####---Header------------------------------------------------------------------
__author__ = "Witold.Jaworski"
__url__ = ("http://www.blender.org")
__email__ = ("witold-jaworski@poczta.neostrada.pl")
__version__ = "1.00"
__bpydoc__ = """\
Use this script to align view to any of its 6 sides (left, right, front, ...).
There is a standard Blender command - View\Align selected, but it always place 
the viewer perpendicular to object's XY plane. This script allows you to choose 
the side of selected object.
"""
####imports-----------------------------------------------------------------
import Blender
####constants---------------------------------------------------------------
MENU = """Object plane:%t
|Top (XY)%x1
|Bottom (YX)%x2
|Front (XZ)%x11
|Rear (ZX)%x12
|Left (YZ)%x21
|Right (ZY)%x22
"""
#menu option names (see the %xs from MENU-just to make the code more readable):
TOP	 = 1
BOTTOM = 2
FRONT = 11
REAR	= 12
LEFT	= 21
RIGHT = 22
 
####module functions-----------------------------------------------------------
def ChangeViewTo(obj, option):
	"""Changes current view
		Arguments:
		obj:	selected object 
		option: code for the plane (int), selected from popup menu by user
	"""
	rot = obj.getMatrix("worldspace").rotationPart()
 
	#axes of additional rotation:
	x = Blender.Mathutils.Vector(1,0,0)*rot #object's local x axis
	rx = Blender.Mathutils.RotationMatrix(-90,3,"r",x) #a rotation: a 1/4 around x
	z = Blender.Mathutils.Vector(0,0,1)*rot #object's local z axis
	rz = Blender.Mathutils.RotationMatrix(-90,3,"r",z) #a rotation: a 1/4 around z
 
	matrix = rot.invert() #rotation matrix for the view
			 #we will add to it additional rotations, when needed
 
	if option == TOP:pass #object matrix is oriented on this plane-no change needed
	elif option == BOTTOM: 	matrix = rx*rx*matrix
	elif option == FRONT:	matrix = rx*matrix
	elif option == REAR:	matrix = rz*rz*rx*matrix
	elif option == LEFT:	matrix = rz*rx*matrix
	elif option == RIGHT:	matrix = rz*rz*rz*rx*matrix
	Blender.Window.SetViewQuat(matrix.toQuat())
	Blender.Window.RedrawAll()
 
def main():
	"""main procedure of this script
	 It handles the interaction with the user
	"""
	if not Blender.Object.GetSelected():
		Blender.Draw.PupMenu("Cannot align view:%t|There is no object selected")
	else:
		option = Blender.Draw.PupMenu(MENU)
		if option > 0 : ChangeViewTo(Blender.Object.GetSelected()[0],option)
 
#let's run the whole thing:
main()