A Few Notes About Python Static Class Member

class Vertebra(object):
    weight = 0.1
    def __init__(self):
        print 'init vertebra'
                
class Fish(Vertebra):
    def __init__(self):
        super(Fish, self).__init__()
        print 'init fish'
        
class Bird(Vertebra):
    def __init__(self):
        super(Bird, self).__init__()
        print 'init bird'
        
class Kingfisher(Bird):
    def __init__(self):
        super(Kingfisher, self).__init__()
        print 'init kingfisher'

Any variable placed right after the class definition will be kinda static. Access the variable by <Class>.<Variable>, i.e.

Vertebra.weight

Any derived class will its <Class>.<Variable>, inherited from its parent class until it has been set specifically. i.e.

Fish.weight
Bird.weight
Kingfisher.weight

Any instance of the class will have a variable of its own, <Instance>.<Variable>, which equals to the value of <Class>.<Variable> until it has been set specifically.

tuna = Fish()
tuna.weight

Any changes to <Class>.<Variable> will affect all relevant variables of all derived classes.

Vertebra.weight = 99.0
Kingfisher.weight

Any changes to <Class>.<Variable> will only affect the values of <Instance>.<Variable> of all existing instances of the class and derived classes, except those have been set specifically.

sootyTern = Bird()
yellowBill = Kingfisher()
sootyTern.weight()
yellowBill.weight()
Bird.weight = 39.1
sootyTern.weight()
yellowBill.weight()

Set value to <Class>.<Variable> will liberate it from changes of inherited class.i.e.

Fish.weight = 1405.9
Vertebra.weight = 0.74
Fish.weight

Set value to <Instance>.<Variable> will liberate it from changes of instanced class and inherited class.i.e.

yellowBill.weight = 463.3
Bird.weight = 239.1
yellowBill.weight

 

Advertisements

Leave a comment

Filed under Python

Hybriding Qt with PyQt

A hybrid Qt and PyQt application sounds attractive: within a Qt application, parts for duty calculation are written in C++ , and UI is written in PyQt, making the application scriptable and expandable. To create such a program, we need to know how to do two sets of things: one is to embed PyQt into a C++ application, the other is to send information from PyQt back to C++. Above is an example. First line is in C++, and second is in PyQt. When the dial or slider is changed, numbers related field will be updated.

So how does it work?

Let’s start with the central widget of the main window.

Center::Center()
{
    setObjectName("center");
    QVBoxLayout* box = new QVBoxLayout();
    LeftWidget *leftDial = new LeftWidget();
    box->addWidget(leftDial);
    ShakeHole *rightDial = new ShakeHole();
    box->addWidget(rightDial);
    setLayout(box);
}

It has two widgets: a LeftWidget and a ShakeHole. (Sorry for the confusing names.)

First, the ShakeHole:

class ShakeHole : public QWidget
{
public:
    ShakeHole();
    ~ShakeHole() {}   
};
ShakeHole::ShakeHole()
{

    int wnd = winId();
        cout<<"winid "<<wnd<<endl;

    cout<<"version "<<Py_GetVersion()<<endl;

    FILE *fin = fopen("/Users/jianzhang/aphid/hybrid_qtpyqt/foo.py","r+");
    PyRun_SimpleFile(fin,"foo");

        PyObject *mainDict = PyModule_GetDict(PyImport_Import(PyString_FromString("__main__")));

        PyObject *claus = PyDict_GetItemString(mainDict, "Ui_Form");

        PyObject* snakewig = Py_BuildValue("i", wnd);

        PyObject * pTuple = PyTuple_New (1);
        PyTuple_SetItem (pTuple, 0, snakewig);

        PyObject *clausInstance = PyObject_CallObject(claus, pTuple);

        cout<<"finished python ui!\n";

        PyErr_Print();

}

It is derived from QWidget, and hooks a PyQt widget within via Python C API.

Now LeftWidget:

class LeftWidget : public HWidget
{
public:
    LeftWidget();
    void setAttribute(const char *attributeName, int value);
private:
    QLineEdit *attributeEdit;
    QLineEdit *attribute1Edit;
};

LeftWidget::LeftWidget()
{
    setObjectName("SnakeHole");
    QHBoxLayout* box = new QHBoxLayout();
    box->addWidget(new QLabel("qt host"));
    attributeEdit = new QLineEdit();
    box->addWidget(attributeEdit);
    attribute1Edit = new QLineEdit();
    box->addWidget(attribute1Edit);
    setLayout(box);

}

void LeftWidget::setAttribute(const char *attributeName, int value)
{
    QString attr(attributeName);
    if(attr == "fieldNumber")
    {
        QString str = QString("%1")
             .arg(value);
        attributeEdit->setText(str);
    }
    else if(attr == "sliderNumber")
    {
        QString str = QString("%1")
             .arg(value);
        attribute1Edit->setText(str);
    }
}

It is a derived HWidget, and it has two QLineEdit, which will be updated by setAttribute(). OK, so what is HWidget?

class HWidget : public QWidget
{
public:
    HWidget();
    virtual ~HWidget();
    virtual void setAttribute(const char *attributeName, int value);
};
void HWidget::setAttribute(const char *attributeName, int value)
{
    qDebug()<<"beep";
}

It is a derived QWidget as well, and it has a virtual function setAttribute() to override.

Go to the PyQt side:

from PyQt4 import QtCore, QtGui
import hoatzin

class Ui_Form(QtGui.QWidget):
    def __init__(self, parentId):      
        Form = QtGui.QWidget.find(parentId)
        super(Ui_Form, self).__init__(Form)
        self.resize(400, 100)
        self.host = hoatzin.Hoatzin()
        self.boxLayout = QtGui.QHBoxLayout()
        self.boxLayout.setObjectName('ui_form_box')
        self.boxLayout.setObjectName("boxLayout")
        self.boxLayout.addWidget(QtGui.QLabel('pyqt widget'))
        self.dial = QtGui.QDial()
        self.boxLayout.addWidget(self.dial)

        self.slider = QtGui.QSlider(QtCore.Qt.Horizontal)
        self.boxLayout.addWidget(self.slider)

        self.setLayout(self.boxLayout)

        self.dial.valueChanged.connect(self.dialTest)
        self.slider.valueChanged.connect(self.sliderTest)

    def dialTest(self):
        w = self.host.set_attribute_int('SnakeHole', 'fieldNumber', self.dial.value() )
        if w != 1:
            print 'SnakeHole not found'

    def sliderTest(self):
        w = self.host.set_attribute_int('SnakeHole', 'sliderNumber', self.slider.value() )
        if w != 1:
            print 'SnakeHole not found'

OK, it is a QWidget and will be attached to a specific parent. It has a dial and a slider, each of them will trigger a function by the signal of valueChanged. Wait a second! What on earth is hoatzin?

It is a Python extension to send information back to the host C++ program:

class Hoatzin
{
public:
    Hoatzin();

    int set_attribute_int(const char *widgetName, const char *attributeName, int value);

private:

};
int Hoatzin::set_attribute_int(const char *widgetName, const char *attributeName, int value)
{
    foreach (QWidget *widget, QApplication::allWidgets())
    {
            if(widget->objectName() == QString(widgetName))
            {
                HWidget *h = (HWidget *)widget;
                h->setAttribute(attributeName, value);
                return 1;        
            }          
    }
    return 0;
}

In set_attribute_int() function, it will search the specific QWidget by objectName(). If found, cast to HWidget, and call its setAttribute(). That is why the target widget should have a specific object name. When it calls “SnakeHole” to setAttribute(), the derived LeftWidget will be called.

Now you know all the tricks necessary for this stunt. The rest is how to compile and test.

First you will need file in https://github.com/spinos/aphid/tree/master/hoatzin

Follow the readme to build the Python module of hoatzin.

Second you need files in https://github.com/spinos/aphid/tree/master/hybrid_qtpyqt

Qmake will create the project for you. It links the compiled library of hoatzin, so no need to compile the source of HWidget again.

Just a confusing example, may be helpful.

Leave a comment

Filed under C++, Python, Qt

PyQt4 Inside Maya2011

Since the version of 2011, Maya User Interface is based on Qt. Using PyQt4 inside Maya2011 should be easier. Before version 2011, PyQt4 is hooked to Maya by pumpThread, and it is not a part of Maya UI, and will always annoyingly drop to background as soon as the Maya UI is focused.  Maya2011 can parent a QMainWIndow to Maya main window, making it a part of Maya UI. Focusing problem is gone.

Like versions before it, Maya2011 installation doesn’t include PyQt4. Install PyQt4 separately, and copy PyQt4 directory and SIP files to Maya2011/Python/Lib/site-packages/. The catch is the latest build of PyQt4 won’t work. Maya2011 is compiled against a specific version of Qt (4.6?). Those Qt dlls reside in Maya2011/bin/ won’t match PyQt4 compiled agianst newer versions of Qt, you will have DLL errors! And those dlls cannot be overridden, or Maya will fail to load. The matched version of PyQt4 should be PyQt-Py2.6-gpl-4.6. I found a copy for 64-bit application here. To test if it works, open mayapy.exe in Maya2011/bin/, and run:

from PyQt4 import QtCore

If no error appears, everything will be fine.

Revised: Maya2011 is compiled against Qt4.7 per doc. So I think any PyQt4 build not newer than 4.7 will do.

Leave a comment

Filed under Maya API, Python

Calculate Angle of View

float $aperture = `getAttr perspShape.hfa`;
float $fl = `getAttr perspShape.focalLength`;
float $fov = $aperture * 0.5 / ( $fl * 0.03937 );
$fov = 2.0*atan($fov) / 3.14159 * 180.0;
print(" angle of view in degrees: " + $fov);

This piece calculates angle-of-view of a camera from its Focal Length and Horizontal Film Aperture.

1 Comment

Filed under MEL

Maya Get Current Camera

Still a bit of maybe useful piece of MEL to find out which camera you are working with. Focus on a model panel, in which you can have a camera to tumble/pan/zoom, and run getCurrentCamera, you will have the transform name of the camera. If you are in some other type of panel, say outliner, empty string will be returned.

global proc string getCurrentCamera()
{
  string $camera;

  // First get the panel that currently has focus
  string $panel = `getPanel -wf`;

  // Compare the panel's type to confirm it is a "modelPanel"
  if ( "modelPanel" == `getPanel -to $panel` )
  {
    // Get the name of the camera using the ‘modelEditor’ command
    $camera = `modelEditor -q -camera $panel`;
  }
  if($camera == "")
      return "";
  if(`nodeType $camera` == "camera")
  {
       string $rels[] = `listRelatives -p $camera`;
       $camera = $rels[0];
       }
  return $camera;
}

2 Comments

Filed under MEL

Maya Python Access String Attribute

Maya Python API has no MString inplementation, so

a = data.inputValue(someNode.someAttr).asString()

will fail. Use a MFnStringData to extract the string carried by the attribute:

textData = data.inputValue(someNode.someAttr).data()
textFunc = OpenMaya.MFnStringData(textData)
a = textFunc.string()

where a will be a normal Python string.

Leave a comment

Filed under Maya API, Python

Build OGRE On Mac OSX 10.5.8

It seems I have problem building OGRE 1.7.3 source on Mac OSX 10.5.8, even the .xcodeproj file generated by CMake cannot be opened, which suggesting a usual suspect: XCode 3.0 is too old for this job. So I tried the older version 1.6.5. It comes with a .xcodeproj file I can open. After redirecting Base SDK Path to MacOSX10.5.sdk, most targets are built without error.

Leave a comment

Filed under Uncategorized