Archive for February, 2011

Using a Custom Python Module With Blender

I’ve reached the stage in my blender project that I need to interface blender with a bunch of pre-existing libraries that we have which are written in C++. This is a better way to work with our system than directly interfacing to the database as our C++ libraries provide a common API with other systems that are using the same database. I am also eventually going to be doing more advanced things with the blender model such as interacting with our simulator system so it’s worth knowing how to do this now.

Fortunately, python provides a way to make bindings to a C/C++ library. Also, blender is happy to import any python module that is compiled against the same version of python that blender has built-in. In my case this is blender 3.1.2 and I’ve already tested this using the py-postgresql module (see previous posts: Blender Project and Connecting to a Database From Blender)

Python has this helpful documentation to get you started on writing a C/C++ module. I followed these instructions and had a python module working without any trouble once I managed to link in all the dependant libraries and set the include paths. I tested this by trying out the module from the python3 console on the linux command line.

As predicted, running from blender was seamless building on the work I’d done previously to get the sys.path right. I imported my module in my blender script and ran my module function with no problems.

Leave a Comment

Duplicating Blender Objects in Python

The aim of this next exercise is to have a bunch of prepared blender objects which I can use as building blocks for my overall model.

In order to differentate my base objects from other in the doc, I’ll use a naming convention of prepending the string ‘My_’ e.g. My_Car, My_Button, My_Cloth. I have a scene panel called My New Objects with some parameters for object creation: my_model, my_name, my_external_db_key. I’ve already worked out how to edit these in a panel and have a button called ‘Create New My Object’ that launches an operator with the scene as the context. The operator reads the scene custom properties.

The my_model property will have a value like ‘Car’, ‘Button’, or ‘Cloth’. I can now write some code to locate an object called “My_” + my_model and duplicate it.

The relevant process seems to be


      select_layers = [False] * 20
      select_layers[4] = True
      bpy.context.scene.layers = select_layers
      src_name = "My_" + obj_model
      found_model = False
      for src_obj in bpy.data.objects:
        if src_obj.name == src_name:
          bpy.ops.object.select_all(action='DESELECT')
          bpy.ops.object.select_name(name=src_name)
          bpy.ops.object.duplicate_move()
          new_obj = bpy.context.scene.objects.active
          new_obj.name = obj_name
          select_layers = [False] * 20
          select_layers[0] = True
          new_obj.layers = select_layers
          bpy.context.scene.layers = select_layers
          found_model = True
          break

 

Notice that I’m also mucking about with layers here. My reference object is in layer 4 so first I change the scene there to find it. Then once I’ve duplicated it and renamed it, I move it to layer 0 and switch the scene view back to layer 0 (I probably should save the original context.scene.layers and restored it at the end of the script)

Anyway, now I can work on adding some more properties to the object that I can set and also calling into an external library to setup extra bits in our database.

Comments (1)

Blender Operator With Input Parameters

Carrying on with my Blender project (which you can find by looking at the blender tag) I was up to the point of needing to supply user entered parameters to the operator so I could generate some rows in my database.

A good resource is the scripts/op directory in the blender distro which has many examples of operators in blender. What I found here was that while it’s possible to run a modal popup window from an operator, it’s not the done thing. Operators typically use default values and properties that have been set via a panel.

Looking at my workflow, I came up with the following steps:

  1. import base drawing on which to build the model
  2. construct the 3D model
  3. Set parameters of model in external database


For the initial creation of a model, I’ll need a panel in the scene which lets me set which of my custom objects will be created (the range of various models will change from project to project so it would be nice if I could load them from a file and configure them from a database). So I’ll type the name of the type of object I want to create and the name / label / MyID of the object and trigger the ‘createMyObject’ operator which will:
  1. load the blender object from a file
  2. call into my external python library to create some initial records for the object in the database
  3. query the database to load the properties that were created in the previous step and create properties for them on the object


I can then move the new object around and change it’s various properties. When I’m done, I’ll run another operation updateMyObject which will update the values in the database from the properties of the object. I suppose I’ll also need a refreshMyObject that will load the values from the DB into the properties of the object. I would do all this with callbacks but the blender docs tell me I can’t get callbacks on custom properties yet.

So going back to the drawing board, I need a scene panel to set the initial values for making a new MyObject. After mucking about for a bit, I found the chickenblender.com / Blender Python Tutorals site. I ended up using the City Destroyer example almost verbatim as a framework for my MyObjectCreator panel and operator.

Leave a Comment

Blender Custom Operator

I’m working on a project to link a blender model with a database. In the first step, I was able to make blender load and display some values from the database based on the name of the selected blender object. The next step is to be able to make blender generate some database records from the blender model. As far as I can tell, the best way to do this is using a custom operator. The operator is executed by selecting an object and pressing the spacebar which brings up a list of operators which I can run.

From the API examples, I put together a little bit of code:

class my_generate_resource(bpy.types.Operator):
    bl_idname = "object.my_generate_resource"
    bl_label = "Generate Resource"
 
    def execute(self, context):
        obj = context.object
        self.report({'WARNING'}, "Generating resource " + obj.name)
        # here I need to do something
        return {'FINISHED'}

 

But how do I make this operator ask me for values that I want to use when generating the database records? I have access to the object name but I also want to type in a value to set in the database.

Maybe I could setup these values in my custom properties panel but then I run into another problem: it seems you can’t create custom properties from a panel, although you can bind a text entry field to an existing custom property. So I’m assuming I can do it from an operator:


class my_test(bpy.types.Operator):
    bl_idname = "object.my_test"
    bl_label = "My test"
 
    def execute(self, context):
        obj = context.object
        obj["my_prop"] = 42
        return {'FINISHED'}

 

I can test if this works by adding a check to my custom panel:

if "my_prop" in obj:
    row = layout.row()
    row.label(text="my_prop: "+str(obj["my_prop"]))

 

The answer is yes: I can create custom properties on my objects from a Operator but not from a Panel (at least not in the draw method). There must be a way I can get a pop-up form from my Operator to type in params.

Leave a Comment

Connecting to a Database from Blender

Continuing on from yesterdays exercise in getting Blender to see a python module on my system, I’m now able to connect to a database from within Blender using the py-postgresql module.

# using DB API 2.0
import postgresql.driver.dbapi20
db =  postgresql.driver.dbapi20.connect(user='blah',
                                password='blahblah',
                                database='blah',
                                host='yada.blah')
cursor = db.cursor()
cursor.execute('select * from foo where bar = \'' + obj.name + '\'')
cursor.fetchall()
 

I also tried using the py-postgresql specific interface which worked just as well and has a few more options:
import postgresql.driver as pg_driver
db =  pg_driver.connect(user='blah',
                                password='blahblah',
                                database='blah',
                                host='yada.blah',
                                port=5432)
ps = db.prepare('select * from foo where bar = \'' + obj.name + '\'')
ps()
 

I could then use the values extracted from the database in my custom panel which updates depending on which object is selected.

Leave a Comment

Interrupted Maverick Upgrade

I found this old draft of a post I made a little while ago when I was upgrading my laptop and thought it might help anyone who ends up in the same situation.

I was almost through my upgrade to Maverick on the second computer when I was called away from work and had to pack up my laptop in a hurry. I closed the lid hoping it would restore ok and continue on where it left off. Unfortunately when I opened the lid again at home, the system sort-of came back but I had no mouse or keyboard and eventually X died killing the Update Manager.

After rebooting, I found I couldn’t log in graphically (though thank goodness I could get a tty shell by pressing ctrl-F1). After rooting around in /var/logs for a bit, I decided that gdm was borked so I tried typing sudo apt-get install gdm as a start on that issue. Lo and behold, a whole heap of other apt install scripts started running and my system continued to upgrade all of the packages that must have been in the queue when it died previously.

Leave a Comment

Blender Project

I’m sharing a few notes here on some work I’m doing with Blender. The task is to model some physical objects that we control in Blender and then autogenerate a lot of information that we can use to make a simulator for testing our controller, the data used by the controller and the scada monitoring system.

Yesterday I downloaded the beta version of Blender as it has a revised GUI and python API which I think will make my life much easier. Using the example in the API Intro I was able to quickly make a custom panel that displays the name of the currently selected object.

The next cab off the rank is to connect to our configuration database and pull out any records that we have configured for our blender model. Specifically we want to assign a class to this model which describes how it is controlled and that in turn will enable us to pull up a list of configuration variables.

To do this I started by installing the python-pygresql package for ubuntu and reading a tutorial on Linux Journal. The first derailment was almost immediate:

import pgdb
Traceback (most recent call last):
  File "<blender_console>", line 1, in <module>
ImportError: No module named pgdb
 

After a few googles I was led to understand that blender has a bundled version of python with limited built in modules. To use the full power of python I had to install the version that python was bundled with natively on my system and then compile in any modules I wanted for that version. Looking at the console for blender, it gave the version as 3.1.2.

I managed to find a python3 package in ubuntu’s synaptic package manager and installed that but this made no difference so I ended up leaving a message on the blender forums which is where I’m leaving things for today.

UPDATE: The solution to the hurdle of not being able to load the pgdb module from blender is documented on the linked forum post but here is the summary:


1) The pgdb module is python 2.6 only
2) I had to compile an alternative module py-postgresql from source as there are no .deb packages that work with python3
3) py-postgresql installed ok but the install script left the read permissions as root-only. Python reported that the module didn’t exist which I guess is a generic way of saying it couldn’t read the module library files.

I also misunderstood the way blender interacts with the system python version. It seems to always run the bundled version of python but you can add the search paths using PYTHONPATH so that it will load external modules you have installed yourself. As long as the python versions are close, the modules will load ok.

Once I had py-postgresql installed correctly, I was able to set PYTHONPATH to include /usr/local/lib/python3.1/dist-packages/ and import py-postgresql and connect to my database from there.

Comments (1)