Friday, December 25, 2009

Merry christmas

In Japan a Christmas day is not a holiday but the New Year's day takes the role, like people gathering at their grandma's house and having a big dinner. Today is my last working day this year. I'm going to go to my parents' house and get scratches on my wrists (*).

I wish you a Merry Christmas.




(*)This one scratches.

Tuesday, December 15, 2009

SIGGRAPH ASIA

The company I'm working for as a freelance is going to exhibit their new GI renderer (at fixstars' booth, E22).

Company web site (Japanese)
CEO/CRO(chief rendering officer) blog (partially English)
CTO blog (Japanese)

fixstars is a company they are working with, which beta released their OpenCL compiler today

And one of my ex-workmate is going to make a presentation (hey Mihai!) about the crowd simulator project we were working on.

Friday, December 4, 2009

Early Preview

What I've been making. Written in pure Python (and wx). Currently it's working only on Linux. I'm also making windows version but it's still quite buggy.

Finding missing files in a sequence.












Fancy template


















Image node








List union/intersection node








Macro example


















union/intersection node code


















Opens every files in a directory, replaces every occurrence of string ".jpg" to ".png", save it to the same file.








System command example.
directory node outputs a list of containing files,
getElement takes list and outputs the elements one by one,
systemCommand executes it, outputs the result













http://hohehohe2.sakura.ne.jp/20091127H.mpg(high quality)
http://hohehohe2.sakura.ne.jp/20091127L.mpg(low quality)

Saturday, November 28, 2009

Hey, I'm not dead

I'm a little bit busy these days and cannot update this blog.
Currently I'm making something interesting, (not a CG stuff,
but general thing).
I'll show it here when time comes.


Sunday, November 1, 2009

Real World Haskell

Several month ago I wanted to learn a functional programming and chose Emacs Lisp as a language (see the results A and B), but ELisp was not a good language for me to learn it since I can write any script procedurally. When I said I like languages which are simple and elegant and easy to read, a guy working with me recommended OCaml but it's not good to learn a functional programming with a language that supports procedural way of programming for a lazy man like me. So I chose Haskell. It is used at my workplace so learning it is also practical. I looked at several haskell tuts on the web and found Real World Haskell to be the easiest to read. So far I'm enjoying it but I don't know if I can finish it since priority of learning a functional programming is still not high.
Note: Real World Haskell seems to expect the readers should have a basic knoledge of Python and C.

Thursday, October 29, 2009

Cross platform editor written in Python

http://editra.org/

It's written in Python, It seems to have all the features of wx.STC, multi-lingualized, it has launchers, and it comes with so many lexers 68k assembly, Apache conf, Haskell, LaTex, Lisp, Lua, Matlab, R, Objective-C, VHDL, YAML, ... ofcourse Python.

It's still version 0.5 but looks promising.







































Friday, October 23, 2009

PyQt is nice

I started using PyQt. It's so nice. Unlike Qt, you don't have to worry about Qt specific stuff, qmake, .pro, moc, etc. I started off by converting several examples that come with Qt. I realized I could convert a Qt code to a PyQt script half automatically. I've converted some of WebKit examples and there was almost no difficulty, though it took a little bit of time to be able to call a slot in PyQt script from JavaScript, until I read this (french!) and found @QtCore.pyqtSlot works.

Ah, and Qt Designer is very nice :)

Thursday, October 15, 2009

Water curtain



Qt Designer is so nice by the way.

Wednesday, October 7, 2009

Big typhoon is coming

http://weathernews.jp/radar/index.html (Weather news)

Thursday, September 24, 2009

What every programmer should know about memory

I started reading What every programmer should know about memory.
It's much more advanced than "L2 cache is 10 times faster than memory access" stuff.

Monday, September 21, 2009

Blackbud

It's surprising they are not yet famous in Japan. GOOD.
Blackbud - You Can Run

Sunday, September 13, 2009

Still alive

I'm still alive and this blog is still alive.
Just have nothing to write for now.
I'll find one soon. Meanwhile, have fun with 1:1 scale gundam.

Friday, August 28, 2009

How many outputs in a node, one or more?

That matters.

Tuesday, August 18, 2009

Living Root Bridges

http://rootbridges.blogspot.com/

Fascinated in Qt

I just read some about Qt and soon started fascinated in it. Since the main part of Qt is GUI framework the functionalities Qt offers are roughly the same as wx, if there is on in wx, there's an equivalent in qt (roughly, again). What I am interested is its direct use of C++ methods in a simple and flexible way. wx has event objects, multiple 'bind' types and multiple macros to bind events to callbacks. Qt does it in only two macros, signal, slot, and one method QObject::connect(). It is easy to connect a single event to multiple callbacks, multiple events to a single callback, the programmer doesn't have to be aware of thread, It's so flexible that it can be used in many ways. I looked at Qt WebKit API and found Qt has excellently integrated WebKit, which would have been very difficult if there was no signal-slot mechanism flexibility. Since it doesn't restrict the number and types of arguments of the callbacks, it can be well used for callback type network programming.

I think there's one minor drawback. In wx, what and when wx emits an event is usually explicit in the document. In Qt, the user can easily emits an event so it is not as easy as wx (it's a trade-off). It is also explicit for the events Qt itself emits, but the information is scattered in the document. In wx there's little chance to miss an event type and timing being emit once you get used to reading the document. (if not, read through Event handling overview (Don't miss the difference of event propagation mechanism between command events and other) then 'Events' section of Classes by category. See what kind of events there are in wx, then read the 'Event handling' section of the document of specific window you use, such as wxTextCtrl.)

Saturday, August 1, 2009

Not a nice way to interrupt a sub thread

In my application, the main thread in in charge of UI drawing with wxPython and starting worker threads which runs user defined Python code. If a user defined Python code is buggy the application user needs to stop it. There's a way to raise a KeyboardInterrupt exception in the main thread but unfortunately not the opposite. What I needed to do is interrupting a sub thread from the main thread. I don't know why it doesn't exist. Probably it is a well considered decision but what I need is what Python misses. The API provides a function PyThreadState_SetAsyncExc() which takes a thread id and exception object and raises exception in the thread. So I had to make a wrapper extension.

pySubthreadInterruptTest.c

#include <Python.h>

static PyObject* interrupt(PyObject* self, PyObject* args)
{
    long threadid;
    PyObject* po_exception;
    if(! PyArg_ParseTuple(args, "lO", &threadid, &po_exception))
    {
        return NULL;
    }

    int result = PyThreadState_SetAsyncExc(threadid, po_exception);
    return Py_BuildValue("l", result);
}

static PyMethodDef MethodsDefs[] = {
    {"interrupt", interrupt, METH_VARARGS},
    {NULL, NULL, 0},
};

void initpystit(void){
    (void) Py_InitModule("pystit", MethodsDefs);
}

setup.py
from distutils.core import setup, Extension

module1 = Extension('pystit',
                    sources = ['pySubthreadInterruptTest.c'])

setup (name = 'PackageName',
       version = '1.0',
       description = 'This is a demo package',
       ext_modules = [module1])

test.py
import time, thread
import pystit
def f():
    print 'thread start'
    try:
            for i in range(1000):
                    print i
                    time.sleep(0.001)
    except:
            print 'interrupted'
            raise
    print 'thread end'
tid = thread.start_new_thread(f, ())
time.sleep(0.001)
pystit.interrupt(tid, ValueError)
time.sleep(1)

$ python setup.py build
running build
running build_ext
building 'pystit' extension
gcc -pthread -shared build/temp.linux-i686-2.6/pySubthreadInterruptTest.o -L/usr/lib -lpython2.6 -o build/lib.linux-i686-2.6/pystit.so
$ cd build/lib.linux-i686-2.6/
$ ls
pystit.so  test.py
$ python test.py
thread start
0
1
2
3
interrupted
Unhandled exception in thread started by <function f at 0xb8043e64>
Traceback (most recent call last):
  File "test.py", line 8, in f
    time.sleep(0.001)
ValueError
$ 

It works but I can't say I'm quite satisfied with the solution.

Tuesday, July 28, 2009

Highlevel Mouse Event Converter (wxPython)
















I really needed to make a good one (not a school assignment :) that convers lowlevel mouse events to highlevel ones. When the user left double clicks on a window, the window gets leftdown, leftup, leftdoubleclick, leftup events in this order. It is too low level. In this case all we want is a single double click event.

- A double click event doesn't come with friends.
- Whenever a mouse down event comes, the application can except a following mouse up event (it may be after dragging event stuff or enter/leave window event).
- A series of dragging events are always wrapped by a startdragging and an enddragging event.
- Capture mouse automatically. (See the comment in onButtonDown() method)
- Can set threshold in pixels so that anybody can double click with his trembling hand (e.g. my dad's)

BUG
While dragging, EnterWindow/LeaveWindow occurs when the mouse enter/leaves the parent window on GTK+.

UPDATES
Jul 29: Passing proper event object to mouse down/up handlers.
Jul 31: Get the right mouse position while mouse captured.
Aug 5 : Fixed missing double click detection failure.
Aug 8 : Modified to Register popup menu functions.


"""Low level -> high level mouse event converter."""
import wx

NORMAL = 0
FIRSTWAIT = 1
SECONDWAIT = 2
AFTERDCLICK = 3
MOUSEDOWN = 4
DRAGGING = 5

ID_FIRSTTIMER = wx.NewId()
ID_SECONDTIMER = wx.NewId()

class HighLevMouseEventConv(object):
def __init__(self, eventWindow, t1 = 200, t2 = 100, dragThreshPixel = 3):
"""HighLevelEvent(eventWindow, t1 = 200, t2 = 100, dragThreshPixel = 3)

eventWindow: The window that receives mouse events.
t1, t2: How long it waits to detect is a mouse down is a double click in mili seconds.
dragThreshPixel: Drag moves within this thresh do not produce drag related events.

When you want a popup menu associated with the window create a func to show the menu, register
it using set(Left, Middle, Right)PopupMenuFunc(). These functions should never return value.
"""
self.t1 = t1
self.t2 = t2
self.dragThreshPixel = dragThreshPixel
self.state = NORMAL
self.evwin = eventWindow

self.firstTimer = wx.Timer(eventWindow, ID_FIRSTTIMER)
self.secondTimer = wx.Timer(eventWindow, ID_SECONDTIMER)

dh = self.defaultHandler
l, r, m = wx.MOUSE_BTN_LEFT, wx.MOUSE_BTN_RIGHT, wx.MOUSE_BTN_MIDDLE

self.onHLVMouseDown = {l:dh, r:dh, m:dh}
self.onHLVMouseUp = {l:dh, r:dh, m:dh}
self.onHLVMouseDClick = {l:dh, r:dh, m:dh}
self.onHLVMouseStartDragging = {l:dh, r:dh, m:dh}
self.onHLVMouseDragging = {l:dh, r:dh, m:dh}
self.onHLVMouseEndDragging = {l:dh, r:dh, m:dh}
self.popupMenuFunc = {l:dh, r:dh, m:dh}

self.onHLVMotion = dh
self.onHLVEnterWindow = dh
self.onHLVLeaveWindow = dh

eventWindow.Bind(wx.EVT_MOUSE_EVENTS, self.onMouseEvents)
eventWindow.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self.onMouseCaptureLost)
eventWindow.Bind(wx.EVT_TIMER, self.onFirstTimer, id = ID_FIRSTTIMER)
eventWindow.Bind(wx.EVT_TIMER, self.onSecondTimer, id = ID_SECONDTIMER)


def setOnHLVLeftDown(self, handler):
self.onHLVMouseDown[wx.MOUSE_BTN_LEFT] = handler

def setOnHLVLeftUp(self, handler):
self.onHLVMouseUp[wx.MOUSE_BTN_LEFT] = handler

def setOnHLVLeftDClick(self, handler):
self.onHLVMouseDClick[wx.MOUSE_BTN_LEFT] = handler

def setOnHLVLeftStartDragging(self, handler):
self.onHLVMouseStartDragging[wx.MOUSE_BTN_LEFT] = handler

def setOnHLVLeftDragging(self, handler):
self.onHLVMouseDragging[wx.MOUSE_BTN_LEFT] = handler

def setOnHLVLeftEndDragging(self, handler):
self.onHLVMouseEndDragging[wx.MOUSE_BTN_LEFT] = handler

def setOnHLVMiddleDown(self, handler):
self.onHLVMouseDown[wx.MOUSE_BTN_MIDDLE] = handler

def setOnHLVMiddleUp(self, handler):
self.onHLVMouseUp[wx.MOUSE_BTN_MIDDLE] = handler

def setOnHLVMiddleDClick(self, handler):
self.onHLVMouseDClick[wx.MOUSE_BTN_MIDDLE] = handler

def setOnHLVMiddleStartDragging(self, handler):
self.onHLVMouseStartDragging[wx.MOUSE_BTN_MIDDLE] = handler

def setOnHLVMiddleDragging(self, handler):
self.onHLVMouseDragging[wx.MOUSE_BTN_MIDDLE] = handler

def setOnHLVMiddleEndDragging(self, handler):
self.onHLVMouseEndDragging[wx.MOUSE_BTN_MIDDLE] = handler

def setOnHLVRightDown(self, handler):
self.onHLVMouseDown[wx.MOUSE_BTN_RIGHT] = handler

def setOnHLVRightUp(self, handler):
self.onHLVMouseUp[wx.MOUSE_BTN_RIGHT] = handler

def setOnHLVRightDClick(self, handler):
self.onHLVMouseDClick[wx.MOUSE_BTN_RIGHT] = handler

def setOnHLVRightStartDragging(self, handler):
self.onHLVMouseStartDragging[wx.MOUSE_BTN_RIGHT] = handler

def setOnHLVRightDragging(self, handler):
self.onHLVMouseDragging[wx.MOUSE_BTN_RIGHT] = handler

def setOnHLVRightEndDragging(self, handler):
self.onHLVMouseEndDragging[wx.MOUSE_BTN_RIGHT] = handler

def setOnHLVMotion(self, handler):
self.onHLVMotion = handler

def setOnHLVEnterWindow(self, handler):
"""It can detect the mouse enter/leaves from, to the parent window when the mouse is dragging.
There's no clear way but detecting mouse motion event to enter/leave the window itself
while the mouse is captured to the parent"""
self.onHLVEnterWindow = handler

def setOnHLVLeaveWindow(self, handler):
"""See setOnHLVLeaveWindow doc."""
self.onHLVLeaveWindow = handler

#popupMenu
def setLeftPopupMenuFunc(self, func):
self.popupMenuFunc[wx.MOUSE_BTN_LEFT] = func

def setMiddlePopupMenuFunc(self, func):
self.popupMenuFunc[wx.MOUSE_BTN_MIDDLE] = func

def setRightPopupMenuFunc(self, func):
self.popupMenuFunc[wx.MOUSE_BTN_RIGHT] = func


def defaultHandler(self, ev):
return True

def onButtonDown(self, ev):
if not self.popupMenuFunc[ev.GetButton()](ev):
return

if self.state == AFTERDCLICK:
self.state = NORMAL
self.evwin.CaptureMouse()

#On GTK, I knoticed if the window is a control, its parent gets mouse events when the window
#captures mouse events with CaptureMouse(). To call ReleaseMouse() properly this object receives
#the mouse leftup events sent to the parent window temporarily.
parent = self.evwin.GetParent()
if parent:
parent.Bind(wx.EVT_LEFT_UP, self.onParentButtonUp)
parent.Bind(wx.EVT_MIDDLE_UP, self.onParentButtonUp)
parent.Bind(wx.EVT_RIGHT_UP, self.onParentButtonUp)
parent.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self.onMouseCaptureLost)
parent.Bind(wx.EVT_MOTION, self.onParentMouseEvents)
parent.Bind(wx.EVT_ENTER_WINDOW, self.onParentEntering)
parent.Bind(wx.EVT_LEAVE_WINDOW, self.onParentLeaving)

self.secondTimer.Stop()
state = self.state
if state == NORMAL:
self.button = ev.GetButton()
self.state = FIRSTWAIT
self.posx, self.posy = ev.GetPosition()
self.downevattrs = self._getEventAttrs(ev)
self.firstTimer.Start(self.t1, True)
elif state == SECONDWAIT:
self.state = AFTERDCLICK
self.onHLVMouseDClick[self.button](ev)

def onButtonUp(self, ev):
evwin = self.evwin
if evwin.HasCapture():
evwin.ReleaseMouse()
parent = evwin.GetParent()
if parent:
parent.Unbind(wx.EVT_LEFT_UP)
parent.Unbind(wx.EVT_MIDDLE_UP)
parent.Unbind(wx.EVT_RIGHT_UP)
parent.Unbind(wx.EVT_MOUSE_CAPTURE_LOST)
parent.Unbind(wx.EVT_MOTION)
parent.Unbind(wx.EVT_ENTER_WINDOW)
parent.Unbind(wx.EVT_LEAVE_WINDOW)

self.firstTimer.Stop()
state = self.state
if state == FIRSTWAIT:
self.state = SECONDWAIT
self.upevattrs = self._getEventAttrs(ev)
self.secondTimer.Start(self.t2, True)
elif state == AFTERDCLICK:
self.state = NORMAL
elif state == MOUSEDOWN:
self.state = NORMAL
self.onHLVMouseUp[self.button](ev)
elif state == DRAGGING:
self.state = NORMAL
self.onHLVMouseEndDragging[self.button](ev)
self.onHLVMouseUp[self.button](ev)

def onButtonDClick(self, ev):
self.secondTimer.Stop()
state = self.state
if state == NORMAL:
self.onButtonDown(ev)
elif state == SECONDWAIT:
self.state = AFTERDCLICK
self.onHLVMouseDClick[self.button](ev)

def onMoving(self, ev):
if self.state == NORMAL:
self.onHLVMotion(ev)

def onDragging(self, ev):
x, y = ev.GetPosition()
t = self.dragThreshPixel
state = self.state
if state == FIRSTWAIT:
if -t < x - self.posx < t and -t < y - self.posy < t:
return
else:
self.state = DRAGGING
self.firstTimer.Stop()
self.onHLVMouseDown[self.button](ev)
self.onHLVMouseStartDragging[self.button](ev)
elif state == MOUSEDOWN:
if -t < x - self.posx < t and -t < y - self.posy < t:
return
else:
self.state = DRAGGING
self.onHLVMouseStartDragging[self.button](ev)
elif state == DRAGGING:
self.onHLVMouseDragging[self.button](ev)

def onEntering(self, ev):
self.onHLVEnterWindow(ev)

def onLeaving(self, ev):
self.onHLVLeaveWindow(ev)


def onMouseEvents(self, ev):
ev.Skip()
if ev.Moving():
return self.onMoving(ev)
elif ev.Dragging():
return self.onDragging(ev)
elif ev.ButtonDown():
return self.onButtonDown(ev)
elif ev.ButtonUp():
return self.onButtonUp(ev)
elif ev.ButtonDClick():
return self.onButtonDClick(ev)
elif ev.Entering():
return self.onEntering(ev)
elif ev.Leaving():
return self.onLeaving(ev)

def onMouseCaptureLost(self, ev):
self.evwin.ReleaseMouse()
ev.Skip()

def onFirstTimer(self, ev):
self.state = MOUSEDOWN
downev = self._createMouseEvent(self.downevattrs)
self.onHLVMouseDown[self.button](downev)

def onSecondTimer(self, ev):
self.state = NORMAL
downev = self._createMouseEvent(self.downevattrs)
upev = self._createMouseEvent(self.upevattrs)
self.onHLVMouseDown[self.button](downev)
self.onHLVMouseUp[self.button](upev)

def onParentButtonUp(self, ev):
self._convetMouseEvent(ev)
self.onButtonUp(ev)
def onParentMouseEvents(self, ev):
self._convetMouseEvent(ev)
self.onMouseEvents(ev)
def onParentEntering(self, ev):
self._convetMouseEvent(ev)
self.onEntering(ev)
def onParentLeaving(self, ev):
self._convetMouseEvent(ev)
self.onLeaving(ev)

def _convetMouseEvent(self, ev):
evwin = self.evwin
parent = evwin.GetParent()
x, y = ev.GetPosition()
offsetx, offsety = evwin.GetPosition()
ev.m_x = x - offsetx
ev.m_y = y - offsety
ev.SetEventObject(evwin)

def _getEventAttrs(self, ev):
return [
ev.GetEventObject(),
ev.GetEventType(),
ev.GetId(),
ev.GetTimestamp(),
ev.m_altDown,
ev.m_controlDown,
ev.m_leftDown,
ev.m_middleDown,
ev.m_rightDown,
ev.m_metaDown,
ev.m_shiftDown,
ev.m_x,
ev.m_y,
ev.m_wheelRotation,
ev.m_wheelDelta,
ev.m_linesPerAction]

def _createMouseEvent(self, eventAttrs):
obj, typ, id, time, alt, ctrl, l, m, r, meta, shift, x, y, wr, wd, lpa = eventAttrs
ev = wx.MouseEvent(typ)
ev.SetEventObject(obj)
ev.GetEventObject()
ev.SetEventType(typ)
ev.SetId(id)
ev.SetTimestamp(time)
ev.m_altDown = alt
ev.m_controlDown = ctrl
ev.m_leftDown = l
ev.m_middleDown = m
ev.m_rightDown = r
ev.m_metaDown = meta
ev.m_shiftDown = shift
ev.m_x = x
ev.m_y = y
ev.m_wheelRotation = wr
ev.m_wheelDelta = wd
ev.m_linesPerAction = lpa
return ev


if __name__ == '__main__':
class TestWindow(wx.Button):
def __init__(self, parent):
super(TestWindow, self).__init__(parent, -1, pos = (50, 50), size = (150, 150))

hlev = HighLevMouseEventConv(self, dragThreshPixel = 15)

hlev.setOnHLVLeftDown(self.onHLVLeftDown)
hlev.setOnHLVLeftUp(self.onHLVLeftUp)
hlev.setOnHLVLeftDClick(self.onHLVLeftDClick)
hlev.setOnHLVLeftStartDragging(self.onHLVLeftStartDragging)
hlev.setOnHLVLeftDragging(self.onHLVLeftDragging)
hlev.setOnHLVLeftEndDragging(self.onHLVLeftEndDragging)
hlev.setOnHLVMiddleDown(self.onHLVMiddleDown)
hlev.setOnHLVMiddleUp(self.onHLVMiddleUp)
hlev.setOnHLVMiddleDClick(self.onHLVMiddleDClick)
hlev.setOnHLVMiddleStartDragging(self.onHLVMiddleStartDragging)
hlev.setOnHLVMiddleDragging(self.onHLVMiddleDragging)
hlev.setOnHLVMiddleEndDragging(self.onHLVMiddleEndDragging)
hlev.setOnHLVRightDown(self.onHLVRightDown)
hlev.setOnHLVRightUp(self.onHLVRightUp)
hlev.setOnHLVRightDClick(self.onHLVRightDClick)
hlev.setOnHLVRightStartDragging(self.onHLVRightStartDragging)
hlev.setOnHLVRightDragging(self.onHLVRightDragging)
hlev.setOnHLVRightEndDragging(self.onHLVRightEndDragging)
hlev.setOnHLVMotion(self.onHLVMotion)
hlev.setOnHLVEnterWindow(self.onHLVEnterWindow)
hlev.setOnHLVLeaveWindow(self.onHLVLeaveWindow)

self.lup = True
self.dragstate = 'normal'

def onHLVLeftDown(self, ev):
print 'onHLVLeftDown', ev.GetPosition(), type(ev.GetEventObject()).__name__
if not self.lup: print 'ERROR ' * 5
self.lup = False
def onHLVLeftUp(self, ev):
print 'onHLVLeftUp', ev.GetPosition(), type(ev.GetEventObject()).__name__
self.lup = True
def onHLVLeftDClick(self, ev):
print 'onHLVLeftDClick', ev.GetPosition(), type(ev.GetEventObject()).__name__
def onHLVLeftStartDragging(self, ev):
print 'onHLVLeftStartDragging', ev.GetPosition(), type(ev.GetEventObject()).__name__
if self.dragstate != 'normal': print 'ERROR ' * 5
self.dragstate = 'started'
def onHLVLeftDragging(self, ev):
print 'onHLVLeftDragging', ev.GetPosition(), type(ev.GetEventObject()).__name__
if not self.dragstate in ('started', 'dragging'): print 'ERROR ' * 5
self.dragstate = 'dragging'
def onHLVLeftEndDragging(self, ev):
print 'onHLVLeftEndDragging', ev.GetPosition(), type(ev.GetEventObject()).__name__
if not self.dragstate in ('started', 'dragging'): print 'ERROR ' * 5
self.dragstate = 'normal'
def onHLVMiddleDown(self, ev):
print 'onHLVMiddleDown', ev.GetPosition(), type(ev.GetEventObject()).__name__
def onHLVMiddleUp(self, ev):
print 'onHLVMiddleUp', ev.GetPosition(), type(ev.GetEventObject()).__name__
def onHLVMiddleDClick(self, ev):
print 'onHLVMiddleDClick', ev.GetPosition(), type(ev.GetEventObject()).__name__
def onHLVMiddleStartDragging(self, ev):
print 'onHLVMiddleStartDragging', ev.GetPosition(), type(ev.GetEventObject()).__name__
def onHLVMiddleDragging(self, ev):
print 'onHLVMiddleDragging', ev.GetPosition(), type(ev.GetEventObject()).__name__
def onHLVMiddleEndDragging(self, ev):
print 'onHLVMiddleEndDragging', ev.GetPosition(), type(ev.GetEventObject()).__name__
def onHLVRightDown(self, ev):
print 'onHLVRightDown', ev.GetPosition(), type(ev.GetEventObject()).__name__
def onHLVRightUp(self, ev):
print 'onHLVRightUp', ev.GetPosition(), type(ev.GetEventObject()).__name__
def onHLVRightDClick(self, ev):
print 'onHLVRightDClick', ev.GetPosition(), type(ev.GetEventObject()).__name__
def onHLVRightStartDragging(self, ev):
print 'onHLVRightStartDragging', ev.GetPosition(), type(ev.GetEventObject()).__name__
def onHLVRightDragging(self, ev):
print 'onHLVRightDragging', ev.GetPosition(), type(ev.GetEventObject()).__name__
def onHLVRightEndDragging(self, ev):
print 'onHLVRightEndDragging', ev.GetPosition(), type(ev.GetEventObject()).__name__
def onHLVMotion(self, ev):
print 'onHLVMotion', ev.GetPosition(), type(ev.GetEventObject()).__name__
def onHLVEnterWindow(self, ev):
print 'onHLVEnterWindow', ev.GetPosition(), type(ev.GetEventObject()).__name__
def onHLVLeaveWindow(self, ev):
print 'onHLVLeaveWindow', ev.GetPosition(), type(ev.GetEventObject()).__name__


app = wx.App()
frame = wx.Frame(None, -1, "Title", size = (300, 400))
panel = wx.Panel(frame, -1)
testwin = TestWindow(panel)
frame.Show()
app.MainLoop()

Saturday, July 25, 2009

Click selecting a curve

I wanted a nice connection arrow for my app, not just a concatenation of lines like











Recently wxWidgets got cairo and wxPython got it accordingly. Using wxcairo (wx.lib.wxcairo) I could draw a nice line of connection like this.











But there was one problem, in my app, the user needs to be able to select the connection by clicking it. How can I detect if the mouse point is on a connection? I need to calculate the length between the line and the point. cairo uses 3rd order bezier curve. To get the parameter t of the bezier curve that gives the closest point on the curve to the mouse point, I need to differentiate the distance and solve the equation of t (or I can get the condition that on the closest point the tangent vector and the vector towards the mouse point makes the right angle. It should produce the same result). The problem is, the equation is 5th order and I cannot solve it. I thought if I should use numerical calculation but still the calculation cost is too expensive. So I decided to use linear interpolation. It's good as long as the user doesn't recognize the error. I can use bounding box and it won't be time consuming. Finally I decided to divide the curve where t = 0, 0.05, 0.1, 0.15, 0.3, 0.5, 0.7, 0.85, 0.9, 0.95, 1. It should be enough just for a GUI program.


These are several tests.

Divide the curve in 3, 5, 8, and 10













Close up














Final result

Wednesday, July 22, 2009

Evolution Reel

Evolution Reel

From the site:
Most people want to see how a professional animator struggles... well here's the reel for you. A playblast of my progress on a shot at the end of each production day.


From the video:
ICE AGE DAWN OF THE DINOSAURS is property of Fox and Blue Sky Studios
The following is for educational purposes only. The reel demonstrates the entire process of completing a production shot from the rough poses to blocking, splining, and polish. The shot was created working 7 days a week during "crunch." Some days represent 16 hour workdays while others are only 3 hours on a Sunday.

Tuesday, July 21, 2009

Eclipse




















Wishing for a clear sky.

Task Dependency Diagram

When I make a relatively large scale system (here relatively large means something that takes more than one month to develop), I make a task dependency diagram like this.













Blue ones are tasks that has been done, whites are not, greens are milestones, usually tests. And I put a small icon that indicates what task I'm currently doing.












During the last project I drew it on an A3 paper and put it on the wall so that everybody could see what he was doing in the overall project picture and how one's work was related to others (each had an animal shaped magnet, I was monky ;). I did it so that nobody would feel as if he was a cog by showing the goal and having people recognize it was a team work but it was also good for a communication tool.

This task (pointing at a task) needs some try and error and he may need more time than expected, so after you have done your current work you jump here (pointing at a task) to prepare for it before he finishes this one (pointing at a task) and comes here (pointing at a task).

It really works.

Friday, July 17, 2009

A module file can be loaded twice as two different modules (in Python 2.x)

I'll show you a case where one file (mymodule.py) gets loaded twice as two different modules. I'm sure this is trivial to some people, if you know how to do this, you don't have to read the rest of this entry. I will write it anyway hoping it will help somebody since it could bring a huge confusion if he doesn't know why it happens (huge confusion, according to my experience).

Modules and packages in directories found in sys.path are called toplevel. Those which are not in the directories found in sys.path but in a package are still accessible but not toplevel.

Say you have a package named mypackage in a directory you can find in the PYTHONPATH, and it has a module mymodule. My package is a toplevel and mymodule is not.

$ pwd
/home/kotamura/mytest
$ echo $PYTHONPATH
/home/kotamura/mytest/mytoplevel
$ tree mytoplevel/
mytoplevel/
`-- mypackage
|-- __init__.py
`-- mymodule.py
Let's run Python interactively.
$ python
Python 2.6 (r26:66714, Jun  8 2009, 16:07:26)
[GCC 4.4.0 20090506 (Red Hat 4.4.0-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import mypackage.mymodule as mm
>>> mm.__name__
'mypackage.mymodule'
You can see the module is not a toplevel from its name (it's under mypackage.)
Go down the directories and do it again.
$ python
Python 2.6 (r26:66714, Jun  8 2009, 16:07:26)
[GCC 4.4.0 20090506 (Red Hat 4.4.0-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import mypackage.mymodule as mm
>>> mm.__name__
'mypackage.mymodule'

It's not surprising at all. But you can also import mymodule
>>> import mymodule as m
>>> m.__name__
'mymodule'

Now mymodule is toplevel!, This is because if you run Python interactively Python adds the current directory to sys.path. What is important is that mm and m are different objects even if it is created from the same file mymodule.py
>>> mm.a = 1
>>> m.a = 2
>>> mm.a
1
>>> mm is m
False

It's not the case you will see only when you use interactive console. The same thing happens when you import a module from another module without specifying it from the toplevel.
$ cat weirdImporter.py
import mypackage.mymodule as mm
import mymodule as m
print mm.__name__
print m.__name__

You don't have to be in the directory to run weirdImporter.py. You can run it from anywhere
$ python /home/kotamura/mytest/mytoplevel/mypackage/weirdImporter.py
mypackage.mymodule
mymodule

Again mymodule.pygot imported twice.
import mymodule as m

Python lets weirdImporter import mymodule because it is in the same directory, and since weirdImporter runs in the __main__ module (not mypackage.weirdImporter I mean), it imports mymodule as a toplevel. While
import mypackage.mymodule as mm

imports the mymoduel.py as mypackage.mymodule. So they are different.

A solution many people recommend is that you always import a package/module specicfying the path from the toplevel. It will solve every problem. You can also use "relative import" first introduced in Python2.5 but it brings another confusion until you get used to it.

Finally, Python3.x doesn't let weirdImporter import mymoduel.py only because it's in the same directory. But you still need to be careful not to have the same file be imported more than once from multiple toplevels. Don't forget executing 'python /path/to/foo.py' adds /path/to directory to sys.path.

Tuesday, July 14, 2009

wx.MiniPanel

While I was browsing wxPython demo, I found a really nifty window called wx.MiniFrame.


















I thought it was nice for my app but soon realized it's wx.Frame. I wanted it inside my application to draw a graph. I didn't want one of my nodes to travel outside my application window and join Firefox, so I decided to make a similar one as a panel.


















Can you see which one is MiniFrame and which is MiniPanel?











One that cannot' be outside the application window is MiniPanel!
(well, anybody talked about close but something? I don't know... (works on Windows))















I haven't finished minor tunings, e.g. the title is embedded in the code, but it's already resizable.

Jul. 15 added:
I tested on (Python2.6, wxPython-2.8.10.1, Fedora11) and (Python 2.6.2, wxPython 2.8.10.1, WindowsXP). Not tested on Mac.


import wx
import wx.lib.resizewidget as resizewidget
import cStringIO as StringIO

batten = '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x11\x00\x00\x00\x10\x08\x02\x00\x00\x00\x7fS\x03\x08\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\x00\x00\x00 cHRM\x00\x00z&\x00\x00\x80\x84\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00u0\x00\x00\xea`\x00\x00:\x98\x00\x00\x17p\x9c\xbaQ<\x00\x00\x00`IDAT8OcdX\xf9\x9f\x81d\x00\xd4C*B\xd6\xf0\x1f7@1\x17\xce\x01\xaa\xc7c!\x8a,\xa6\x1edi8\x1b\x9f\x1e\xb8\xeb\x80f\xa1\xb1\x11\xae\xc0j\x0f\xb2\xbf \n\x08\xb8\r\xcd\x06\xa2\xf4\xa0\x05\x1ea=\xe4\xfb\x07\x7f`2P\x14?\xc8^\xc7L\x0f\xd8\xd3\x01\t\xa9\x8e\x04\xa5\xf0\xa4L\x86\x1e\x00\xbc\xf4\xfe\xa6\xcaO\x9a\x16\x00\x00\x00\x00IEND\xaeB`\x82'

bmpstream = StringIO.StringIO(batten)
try:
image = wx.ImageFromStream(bmpstream)
finally:
bmpstream.close()

class _TitleBarPanel(wx.Panel):
def __init__(self, parent, titleFGColour, titleBGColour, barWidth, fontSize, title):
wx.Panel.__init__(self, parent, pos = (0, 0), size = (0, barWidth))
self.SetBackgroundColour(titleBGColour)
self.SetMaxSize((100000, barWidth))
self.parent = parent
self.gparent = parent.GetParent()
self.restwin = None

#Title static text.
self.titlest = titlest = wx.StaticText(self, -1, title, size = (100000, barWidth))
titlest.SetForegroundColour(titleFGColour)
font = titlest.GetFont()
font.SetPointSize(fontSize)
titlest.SetFont(font)

#Close bitmap button.
bfi = wx.BitmapFromImage(image)
self.closeb = b = wx.BitmapButton(self, -1, bfi, (0, 0), (barWidth, barWidth), style = wx.BU_EXACTFIT)
b.SetToolTipString("Delete this node.")

hsizer = wx.BoxSizer(wx.HORIZONTAL)
hsizer.Add(titlest, 1, wx.EXPAND | wx.CENTER)
hsizer.Add(b, 0, wx.RIGHT)
self.SetSizer(hsizer)

self.titlest.Bind(wx.EVT_BUTTON, self.onClose, b)
self.Bind(wx.EVT_BUTTON, self.onClose, b)
self.titlest.Bind(wx.EVT_LEFT_DOWN, self.onTitleMouseLeftDown)
self.titlest.Bind(wx.EVT_LEFT_UP, self.onTitleMouseLeftUp)
self.Bind(wx.EVT_LEFT_UP, self.onTitleMouseLeftUp)
self.titlest.Bind(wx.EVT_MOTION, self.onTitleMouseMotion)
self.Bind(wx.EVT_MOTION, self.onTitleMouseMotion)
self.titlest.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self.onTitleMouseCaptureLost)
self.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self.onTitleMouseCaptureLost)

def onClose(self, ev):
self.parent.Close()

def onTitleMouseLeftDown(self, ev):
self.titlest.CaptureMouse()
x, y = ev.GetPosition()
sx, sy = self.titlest.GetScreenPosition()
gx, gy = self.gparent.GetPosition()
self.px, self.py = sx + x, sy + y
self.gx, self.gy = gx, gy

def onTitleMouseLeftUp(self, ev):
if self.titlest.HasCapture():
self.titlest.ReleaseMouse()

def onTitleMouseMotion(self, ev):
if ev.Dragging():
x, y = ev.GetPosition()
sx, sy = self.titlest.GetScreenPosition()
px, py = sx + x, sy + y
if self.restwin:
fsx, fsy = self.restwin.GetScreenPosition()
fw, fh = self.restwin.GetSize()
margin = self.margin
if px < fsx + margin or py < fsy + margin or px > fsx + fw - margin or py > fsy + fh - margin:
return
self.gparent.SetPosition((self.gx + px - self.px, self.gy + py - self.py))

def onTitleMouseCaptureLost(self, ev):
self.titlest.ReleaseMouse()

def setDragRestrictWindow(self, window, margin):
self.restwin = window
self.margin = margin

class NodePanel(wx.Panel):
def __init__(self, parent, id = wx.ID_ANY, pos=wx.DefaultPosition, size = wx.DefaultSize,
style = wx.TAB_TRAVERSAL|wx.NO_BORDER, name=wx.PanelNameStr,
borderColour = wx.BLACK, minSize = (0, 0), maxSize = (-1, -1),
bgColour = wx.NullColour, titleFGColour = wx.WHITE, titleBGColour = wx.Colour(80, 167, 255),
barWidth = 13, fontSize = 8, title = 'MiniPanel'):
wx.Panel.__init__(self, parent, id, pos, size, style | wx.FULL_REPAINT_ON_RESIZE, name)
self.borderColour = borderColour

rw = resizewidget.ResizeWidget(parent, pos = pos, style = wx.FULL_REPAINT_ON_RESIZE)
self.SetMinSize(minSize)
self.SetMaxSize(maxSize)
rw.SetManagedChild(self)

self.tb = tb = _TitleBarPanel(self, titleFGColour, titleBGColour, barWidth, fontSize, title)
self.clientPanel = clientPanel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(tb, 0, wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border = 3)
sizer.Add(clientPanel, 1, wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT, border = 3)

self.SetSizer(sizer)
self.SetBackgroundColour(bgColour)
clientPanel.SetBackgroundColour(bgColour)

rw.SetSize(size)

self.Bind(wx.EVT_PAINT,self.OnPaint)
self.Bind(wx.EVT_IDLE,self.OnPaintOnIdle)

def OnPaintOnIdle(self, ev):
#self._onPaint(wx.ClientDC(self))
pass

def OnPaint(self, ev):
self._onPaint(wx.PaintDC(self))

def _onPaint(self, dc):
#Use double buffering as you like.
#(wxPython-demo dir)/samples/pySketch/pySketch.py has a good example of it.
dc.SetPen(wx.Pen(self.borderColour))
x, y = self.GetSize()
dc.DrawLine(0, 0, x-1, 0)
dc.DrawLine(0, 0, 0, y-1)
dc.DrawLine(x-1, 0, x-1, y-1)
dc.DrawLine(0, y-1, x-1, y-1)

def setDragRestrictWindow(self, window, margin = 0):
#If you don't set this, the Panel will go outside the window. Is there a nice way? :(
self.tb.setDragRestrictWindow(window, margin)

def getClientPanel(self):
return self.clientPanel

def getTitleStaticText(self):
return self.tb.titlest

def Destroy(self):
#The resize handle is the parent of the Panel(reparented). We want to destroy
#the handle too. We'll override Destroy() to make sure it is destroyed.
self.GetParent().Destroy()


if __name__ == '__main__':
myapp=wx.PySimpleApp()
frame = wx.Frame(None, -1, "Title", size = wx.Size(300, 400))
scroll = wx.ScrolledWindow(frame, -1)
scroll.SetScrollbars(50,50,10,10)
np = NodePanel(scroll, pos = (30, 30), size = (200, 200), borderColour = (128, 128, 128))

np.setDragRestrictWindow(scroll, 15)

print np.getTitleStaticText().GetLabel()

def OnClose(ev):
if ev.CanVeto():
dlg = wx.MessageDialog(None, 'Are you sure?', 'msgb', wx.YES_NO)
if dlg.ShowModal() == wx.ID_YES:
np.GetParent().Destroy()
dlg.Destroy()

else:
np.Destroy()


np.Bind(wx.EVT_CLOSE, OnClose, np)


def CloseIt(ev):
np.Close()
clientAreaCloseButton = wx.Button(np.getClientPanel(), -1, "Close Me", pos = (20, 20))
np.Bind(wx.EVT_BUTTON, CloseIt, clientAreaCloseButton)

frame.Show()
myapp.MainLoop()

Thursday, July 9, 2009

Wednesday, July 8, 2009

Thread Manager

Last update Jun 10

import thread, threading

class _WorkerThread(threading.Thread):
    def __init__(self, sem, job, *args, **kargs):
        self._workSem = sem
        super(_WorkerThread, self).__init__()
        self.job = job
        self.args = args
        self.kargs = kargs
        self.isCanceled = False
        self.hasStarted = False
        self._cancelLock = threading.Lock()

    def run(self):
        if self._workSem:
            with self._workSem:
                with self._cancelLock:
                    if self.isCanceled:
                        return
                    self.hasStarted = True
                self.job(*self.args, **self.kargs)
        else:
            self.job(*self.args, **self.kargs)

class JobManager(object):
    def __init__(self, maxnumthreads = 1):
        """Set maxnumthreads to specify the max number of threads which runs concurrently."""
        self._workers = {}
        self._unmanagedWorkers = []
        self._workSem = threading.Semaphore(maxnumthreads)
        self.maxnumthreads = maxnumthreads
        self._lock = threading.RLock()
        self._nextJobIdCounter = 1

    def postJob(self, job, *args, **kargs):
        """job must be a callable, args and kargs are arguments passed to it."""
        self._gc()
        wt = _WorkerThread(self._workSem, job, *args, **kargs)
        wt.start()
        id = self._nextJobIdCounter
        self._workers[id] = wt
        self._nextJobIdCounter += 1
        return id

    def cancelJob(self, jobid):
        """Cancel a posted job. jobid must be an object returned by postJob().
        It returns True if the job gets canceled, False it it has started."""
        self._gc()
        worker = self._workers.get(jobid, None)
        if not worker: #It doesn't exist because it finished execution and removed from the _workers
            return False
        with worker._cancelLock:
            worker.isCanceled = True
            return not worker.hasStarted

    def waitOnIdle(self):
        """Blocks until the every worker thread terminates."""
        self._gc()
        while self._workers or self._unmanagedWorkers:
            wt = (self._workers.values() + self._unmanagedWorkers).pop()
            wt.join()
            self._gc()

    def getNumWaitingJobs(self):
        """Returns the number of jobs waiting. It includes threads currently running."""
        self._gc()
        return len(self._workers)

    def forceExecuteOnWorkerThread(self, job, *args, **kargs):
        """Execute the job immediately on a thread. It is not queued."""
        wt = _WorkerThread(None, job, *args, **kargs)
        wt.start()
        self._unmanagedWorkers.append(wt)

    def executeWhenNoWorkerThreadsRunning(self, job, *args, **kargs):
        """The calling thread execute the job (callable), ensuring no worker threads running.
        It blocks when a thread is running.It DOESN'T mean job is executed after the every
        worker threads has been terminated.
        (Though it looks the current Python implementation awakens a thread which called acquire()
        earlier.)
        It doesn't take jobs launched by forceExecuteOnWorkerThread() into account."""
        for i in range(self.maxnumthreads):
            self._workSem.acquire()
        try:
            job(*args, **kargs)
        finally:
            for i in range(self.maxnumthreads):
                self._workSem.release()

    def _gc(self):
        with self._lock:
            self._workers = dict([w for w in self._workers.items() if w[1].isAlive()])
            self._unmanagedWorkers = [wt for wt in self._unmanagedWorkers if wt.isAlive()]


if __name__ == '__main__':
    import time
    def somejob(i, wait = 0.1):
        time.sleep(wait)
        print 'somejob', i
        time.sleep(1)
    def endMessage(msg):
        print msg

    jm = JobManager()
    jobIds = []
    for i in range(5):
        id = jm.postJob(somejob, i + 1)
        jobIds.append(id)
        print 'posted', id
    time.sleep(2)
    print "cancel", jobIds[0], jm.cancelJob(jobIds[0])
    print "cancel", jobIds[-1], jm.cancelJob(jobIds[-1])
    #print jm.getNumWaitingJobs()
    jm.forceExecuteOnWorkerThread(somejob, 'unmanaged1', 4)
    jm.waitOnIdle() #It waits for unmanaged1 termination
    jm.forceExecuteOnWorkerThread(somejob, 'unmanaged2')
    jm.executeWhenNoWorkerThreadsRunning(endMessage, 'done') #It doesn't wait for unmanaged2

Thursday, July 2, 2009

Writing a technical document

When I write a technical document, I try to be careful to keep three things.

1) Do not use the term that hasn't been explained in the earlier pages.
I think this is a basic rule that every document must keep, but lots of them break it. I once read a document about a Python framework which uses a term "jelly" (is that case, it used the term in method names) with no explanation beforehand. It took me a long time until I know its meaning. I hoped if the writer had added just one line saying "jelly means serialize". (although the framework itself is very useful. Lots of its terms are named after a sandwich. You guess what it is? ;).

2) Do not try to explain two things at a time.
Human brains are not designed to think about two things at a time. When you explain recursive function call, don't take mandelbrot as an example. Think about an average reader and take an example that most reader is supposed to know.

3) "Make a list of stuffs to write, think about the explanation flow of the document".
I usually make a graph for 1) and 3), which has a set of circles, each circle contains one element of the list , and dependencies as arrows between the circles.

Tuesday, June 30, 2009

Playing .wav files on Windows, Mac, Linux

Playing .wav files in the current directory.

Windows


import glob, winsound, random
wavfiles = glob.glob("*.wav")
wavfile = random.choice(wavfiles)
while(True):
raw_input()
wavfile = random.choice(wavfiles)
winsound.PlaySound(wavfile, winsound.SND_FILENAME)

Mac (got the idea here)

from AppKit import NSSound
from time import sleep
import glob, random
wavfiles = glob.glob("*.wav")
while(True):
raw_input()
wavfile = random.choice(wavfiles)
s = NSSound.alloc()
s.initWithContentsOfFile_byReference_(wavfile, True)
s.play()
while s.isPlaying():
sleep(0.1)
s.stop()

Linux (needs play command)

import os, glob, random
wavfiles = glob.glob("*.wav")
wavfile = random.choice(wavfiles)
while(True):
raw_input()
wavfile = random.choice(wavfiles)
os.system("play -q " + wavfile)

Monday, June 29, 2009

Damien Walters Showreel 2009

Hey look at this!

pydoc look up command (Emacs)

Just add the following stuff in your .emacs.
and type Ctrl-h f at point where you want to look up pydoc.















;;----pydoc lookup----
(defun hohe2-lookup-pydoc ()
(interactive)
(let ((curpoint (point)) (prepoint) (postpoint) (cmd))
(save-excursion
(beginning-of-line)
(setq prepoint (buffer-substring (point) curpoint)))
(save-excursion
(end-of-line)
(setq postpoint (buffer-substring (point) curpoint)))
(if (string-match "[_a-z][_\\.0-9a-z]*$" prepoint)
(setq cmd (substring prepoint (match-beginning 0) (match-end 0))))
(if (string-match "^[_0-9a-z]*" postpoint)
(setq cmd (concat cmd (substring postpoint (match-beginning 0) (match-end 0)))))
(if (string= cmd "") nil
(let ((max-mini-window-height 0))
(shell-command (concat "pydoc " cmd))))))

(add-hook 'python-mode-hook
(lambda ()
(local-set-key (kbd "C-h f") 'hohe2-lookup-pydoc)))

I had been thinking that Python is a toy and the rest of the programming languages are all tools, but I realized Emacs Lisp is a toy too. I'm sure I'm reinventing a wheel. Don't worry!

Saturday, June 27, 2009

Emacs To Maya for Python Scripts

I modified the original Emacs To Maya by slavomir.kaslev.












Feb. 13, 2011.
Sending a command through file is a spec, not cutting corners. Due to Maya command port limitation we need to make a command small enough to have it within one packet (I don't know if the limitation still exisits in Maya 2011, anyone knows it?).

Feb. 6, 2011.
When I posted this entry two years ago, I emailed Slavomir and he replied
I am very happy that you find etom.el usefull. Etom.el is free

software, so you can do whatever you want with it to suit your needs.
so you can feel free to use this patch. Still please keep in mind that this is GPL licenced.

Jan. 23, 2011.
If you get error regarding replace-in-string, it's probably due to missing function on your emacs. you can find a replace-in-string implementation in find-files.el module, made by Robert Fenk.

And on Windows Vista,
commandPort -eo -n ":2222";
doesn't work. Execute
commandPort -eo -n "127.0.0.1:2222";

Tnanks Hajime!


This is not an authorized version by the original author/maintainer, and I only tested on Linux (Fedora 11, Maya 8.5, GNU Emacs 22.3.1)
last modified: Jul. 27, 2012

;;; etom.el --- Emacs to Maya communication

;; Copyright (C) 2007 Slavomir Kaslev

;; Author: Slavomir Kaslev <slavomir.kaslev@gmail.com>
;; Maintainer: Slavomir Kaslev <slavomir.kaslev@gmail.com>
;; Created: 17 Jun 2007
;; Version: etom.el 0.02 dated 07/07/03 at 16:45:51
;; Keywords: emacs, maya, mel

;; Modified the original script by Slavomir Kaslev to send Python scripts
;; Modified By: Koichi Tamura <hohehohe2@gmail.com>
;; Date: 28 Jun 2009

;; This file is NOT part of Emacs.
;;
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License
;; as published by the Free Software Foundation; either version 2
;; of the License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program; if not, write to the Free Software
;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
;; MA 02110-1301, USA.

;; Commentary:

;; This package is used for communication between emacs and Maya. For
;; example you can use it together with Shuji Narazaki's mel-mode to
;; send pieces of mel code to Maya and get the results back in emacs.

;; To use this, insert in your ~/.emacs file:
;; (add-hook
;;  'mel-mode-hook
;;  (lambda ()
;;    (require 'etom)
;;    (setq etom-default-host "localhost")
;;    (setq etom-default-port 2222)
;;    (local-set-key (kbd "C-c C-r") 'etom-send-region)
;;    (local-set-key (kbd "C-c C-c") 'etom-send-buffer)
;;    (local-set-key (kbd "C-c C-l") 'etom-send-buffer)
;;    (local-set-key (kbd "C-c C-z") 'etom-show-buffer)))
;;
;; For Python
;;
;; Add the following script after the above one.
;; (add-hook
;;  'python-mode-hook
;;  (lambda ()
;;    (require 'etom)
;;    (setq etom-default-host "localhost")
;;    (setq etom-default-port 2222)
;;    (local-set-key (kbd "C-c C-r") 'etom-send-region-py)
;;    (local-set-key (kbd "C-c C-c") 'etom-send-buffer-py)
;;    (local-set-key (kbd "C-c C-l") 'etom-send-buffer-py)
;;    (local-set-key (kbd "C-c C-z") 'etom-show-buffer)))


;;; Code:

(require 'comint)

(defcustom etom-default-host "localhost"
"Default name of the host on which Maya is running."
:type 'string
:group 'etom)

(defcustom etom-default-port 2222
"Default port number to connect to Maya."
:type 'integer
:group 'etom)

(defcustom etom-always-show t
"Non-nil means display etom-buffer after sending a command."
:type 'boolean
:group 'etom)

(defcustom etom-prompt-regexp "^\0$"
"Regexp which matches the Maya's prompt."
:type 'regexp
:group 'etom)

(defvar etom-buffer nil
"Buffer used for communication with Maya.")

(defun etom-show-buffer ()
"Make sure `etom-buffer' is being displayed."
(interactive)
(if (not (etom-connected))
(etom-connect))
(display-buffer etom-buffer))

(defun etom-hide-buffer ()
"Delete all windows that display `etom-buffer'."
(interactive)
(delete-windows-on etom-buffer))

(defun etom-connect ()
"Connect to Maya."
(interactive)
(setq comint-prompt-regexp etom-prompt-regexp)
(setq etom-buffer (make-comint "Maya" (cons etom-default-host etom-default-port)))
(set-process-query-on-exit-flag (get-buffer-process etom-buffer) nil)
(if etom-always-show
(etom-show-buffer))
(comint-simple-send (get-buffer-process etom-buffer)
                (concat
                 "python(\""
                 "def etom_pyexec(fname):\\n"
                 "  f = open(fname)\\n"
                 "  try:\\n"
                 "    c = f.read().replace('\\\\r\\\\n', '\\\\n')\\n"
                 "    try:\\n"
                 "      return str(eval(c))\\n"
                 "    except:\\n"
                 "      exec c in globals(), globals()\\n"
                 "  finally:\\n"
                 "    f.close()"
                 "\")")))

(defun etom-disconnect ()
"Disconnect from Maya and kill etom-buffer."
(interactive)
(if etom-buffer
(kill-buffer etom-buffer)))

(defun etom-connected ()
"Return non-nil if there is connection to Maya."
(interactive)
(comint-check-proc etom-buffer))

(defun etom-prompt-line ()
(save-excursion
(forward-line 0)
(looking-at comint-prompt-regexp)))

(defun etom-wait-for-prompt (last-prompt)
(let ((prompt-found nil))
(while (not prompt-found)
(accept-process-output (get-buffer-process (current-buffer)))
(goto-char (point-max))
(setq prompt-found (and (etom-prompt-line) (not (= (count-lines (point-min) (point-max)) last-prompt)))))))

(defun etom-send-current-line ()
"Send current line to Maya."
(interactive)
(let ((start (save-excursion (beginning-of-line) (point)))
(end (save-excursion (end-of-line) (point))))
(etom-send-region start end)))

(defun etom-send-current-line-py ()
"Send current line to Maya as Python."
(interactive)
(let ((start (save-excursion (beginning-of-line) (point)))
(end (save-excursion (end-of-line) (point))))
(etom-send-region-py start end)))

(defun etom-send-region (start end &optional aspython)
"Send region to Maya."
(interactive "r")
(if (not (etom-connected))
(etom-connect))
(if etom-always-show
(etom-show-buffer))
(let (
(tempfile (make-temp-file "etom-"))
(formatstr (if aspython "python(\"etom_pyexec('%s')\")" "source \"%s\";")))
(write-region start end tempfile)
;; send source(tempfile)
(with-current-buffer etom-buffer
(let ((last-prompt (count-lines (point-min) (point-max))))
(goto-char (point-max))
(comint-simple-send (get-buffer-process (current-buffer))
                   (format formatstr
                           (replace-in-string tempfile "\\\\" "\\\\\\\\" )))
(etom-wait-for-prompt last-prompt)
(delete-file tempfile)))))

(defun etom-send-region-py (start end)
"Send region to Maya as Python."
(interactive "r")
(etom-send-region start end t))

(defun etom-send-region-2 (start end)
"Send region to Maya."
(interactive "r")
(if (not (etom-connected))
(etom-connect))
(if etom-always-show
(etom-show-buffer))
(comint-simple-send (get-buffer-process etom-buffer)
             (buffer-substring start end)))

(defun etom-send-buffer ()
"Send whole buffer to Maya."
(interactive)
(etom-send-region (point-min) (point-max)))

(defun etom-send-buffer-py ()
"Send whole buffer to Maya as Python."
(interactive)
(etom-send-region-py (point-min) (point-max)))

(provide 'etom)

;;; etom.el ends here

Friday, June 26, 2009

Emacs To Maya

I started using Emacs To Maya.
Now modifying it to send Python scripts (Half done, thinking about how to get the value printed in the command history).

Ah, I added the following script to .emacs before


(add-hook
'mel-mode-hook ...

to avoid

Symbol's function definition is void: replace-in-string

error.

(if (not (fboundp 'replace-in-string))
(defun replace-in-string (string regexp replacement &optional literal)
"Replace regex in string with replacement"
(replace-regexp-in-string regexp replacement string t literal)))

(got it here)
For those who has seen the same error.

Recursive directory search configuration for anything.el

Continued from the previous entry.
If the selected document is html (".html" or ".htm" case insensitive) it opens the document with w3m in a new frame, otherwise it just opens it normally as a text file in the current frame.


hohe2-anything-find.el


(require 'w3m)
(require 'anything-config)


;;;; Find


(defun hohe2-register-c-source-find (config-symbol title-name find-dir min-required-chars)
"Register a new find configuration"
(set config-symbol
(list
`(name . ,title-name)
`(candidates . (lambda ()
(apply 'start-process "find-process" nil
(list
"find"
,find-dir
"-iname"
(concat "*" anything-pattern "*"))
)))
'(type . findfile)
`(requires-pattern . ,min-required-chars)
'(delayed))
)
)


(defun hohe2-anything-html-open-method (c)
(select-window (frame-selected-window (make-frame)))
(w3m-browse-url (concat "file://" c)))

(defun hohe2-anything-default-open-method (c)
(let ((ext (file-name-extension c)))
(if ext
(if (member (downcase ext) '("html" "htm"))
(hohe2-anything-html-open-method c)
(find-file c)))))

(add-to-list 'anything-type-attributes
'(findfile (action
("Default" . hohe2-anything-default-open-method)
("Browse other window" . (lambda (c)
(hohe2-anything-html-open-method c)))
("Browse" . (lambda (c) (w3m-browse-url (concat "file://" c))))
("Browse with Firefox" . (lambda (c) (shell-command (concat "firefox file://" c))))
("Open as text" . find-file)
)))



(provide 'hohe2-anything-find)

anything.el for Maya document (continued)


For those who uses emacs for Maya programming :)

Internally it uses "find". It is not limited to Maya documents. you can search for documents in any directory (and its sub directories recursively).

Usage:
Type META-; (I made a shortcut that searches only Maya API documents)
See the movie in another blog entry.

Installation:
1) If you haven't installed anything.el install it.

2) Install w3m web browser for Emacs. I just executed


sudo yum install w3m-el

3) Add hohe2-anything-find.el in any directory.

4) Add this line to .emacs, (unless the directory path has been added already)

(add-to-list 'load-path "path/to/directory")
5) Add the following script to your .emacs file. (Change the paths to the document directories).

;;Additional anything config for Maya.
(hohe2-register-c-source-find
'hohe2-anything-c-source-find-mayaapi "Maya API" "/usr/autodesk/maya/docs/Maya8.5/en_US/API" 2)

(hohe2-register-c-source-find
'hohe2-anything-c-source-find-mayamel "Maya MEL" "/usr/autodesk/maya/docs/Maya8.5/en_US/Commands" 2)

(hohe2-register-c-source-find
'hohe2-anything-c-source-find-mayapython "Maya Python" "/usr/autodesk/maya/docs/Maya8.5/en_US/CommandsPython" 2)

(hohe2-register-c-source-find
'hohe2-anything-c-source-find-mayanode "Maya Node" "/usr/autodesk/maya/docs/Maya8.5/en_US/Nodes" 2)

(setq hohe2-anything-alternative-sources (list hohe2-anything-c-source-find-mayaapi
hohe2-anything-c-source-find-mayamel
hohe2-anything-c-source-find-mayapython
hohe2-anything-c-source-find-mayanode
))

(defun hohe2-anything-alternative ()
(interactive)
(anything hohe2-anything-alternative-sources);;)
)
(global-set-key (kbd "M-;") 'hohe2-anything-alternative)
6) Restart Emacs.

7) Enjoy programming if it works.

This is my development environment

2.6.29.4-167.fc11.i686.PAE #1 SMP Wed May 27 17:28:22 EDT 2009 i686 i686 i386 GNU/Linux
Gnome 2.26.2
GNU Emacs 22.3.1

No guarantee. Use it at your own risk.

hohe2-anything-find.el

(require 'w3m)
(require 'anything-config)


;;;; Find


(defun hohe2-register-c-source-find (config-symbol title-name find-dir min-required-chars)
"Register a new find configuration"
(set config-symbol
(list
`(name . ,title-name)
`(candidates . (lambda ()
(apply 'start-process "find-process" nil
(list
"find"
,find-dir
"-iname"
(concat "*" anything-pattern "*"))
)))
'(type . findfile)
`(requires-pattern . ,min-required-chars)
'(delayed))
)
)


(add-to-list 'anything-type-attributes
'(findfile (action
("Browse other window" . (lambda (c)
(select-window (frame-selected-window (make-frame)))
(w3m-browse-url (concat "file://" c))))
("Browse" . (lambda (c) (w3m-browse-url (concat "file://" c))))
("Browse with Firefox" . (lambda (c) (shell-command (concat "firefox file://" c))))
("Open as text" . find-file)
)))



(provide 'hohe2-anything-find)

Currently it assumes every document is HTML. I will modify the script so that it looks at the file extension to know how to open the file soon(er or later). If you modify part of the above code like this it opens the file as a text by default.
June 28 added. Done.

(add-to-list 'anything-type-attributes
'(findfile (action
("Open as text" . find-file)
("Browse other window" . (lambda (c)
(select-window (frame-selected-window (make-frame)))
(w3m-browse-url (concat "file://" c))))
("Browse" . (lambda (c) (w3m-browse-url (concat "file://" c))))
("Browse with Firefox" . (lambda (c) (shell-command (concat "firefox file://" c))))
)))