メイン | 2007年05月 »

2007年04月 アーカイブ

2007年04月13日

はじめまして

4月からサイボウズ・ラボに入社しました西尾泰和です。

個人のブログは西尾泰和のブログ。 ぬるい話は今まで通り個人ブログに書くので、 こちらではラボの業務時間を使って調べたTIPSなどを中心に書いていこうかと思っています。 よろしくお願いします。

Pythonで日本語PDFをつくる短いサンプル

まずReportLab - Open Source Softwareで公開されているReportLab Toolkitをダウンロードしてインストールします。 下の13行でちゃんと日本語で「こんにちは世界!」と書かれたPDFが出力されます。

これは「位置を指定してそこに文字列を書く」というお絵かき的な方法ですが、 reportlab.platypusを使うと長文を折り返して表示するような書類的な方法も可能です。

# -*- coding: cp932 -*-
from reportlab.pdfgen import canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.cidfonts import UnicodeCIDFont

fontname = "HeiseiKakuGo-W5" # or "HeiseiMin-W3"
pdfmetrics.registerFont(UnicodeCIDFont(fontname))
c = canvas.Canvas("hello.pdf")
c.setFont(fontname, 30)
c.drawString(100, 675, u"こんにちは世界!")
c.showPage()
c.save()

print "ok." 

2007年04月14日

PythonワンライナーでUSBミサイルランチャーを制御

先週の金曜日、弊社の 竹迫鶴岡 と共に 弾さんのお宅で行われた Hackathonに参加しました。 内容に関しては YappoLogs: YAPC::Asia Hackathon at Dan the hotelが詳しいです。

新人研修にかまけて記事にせずに放置していたところ TokuLog 改め Perl を極めて結婚するブログ - missile on ruby.でリークされてしまいましたが、ワンライナーで作りました。 下のコードを実行すると、動作テストとして三三七拍子を奏でた後、 インタラクティブに操作できるモードになります。

[globals().__setitem__("COMMAND",{'right':[8,0,0,0,0,0,0,0],'d':[2,0,0,0,0,0,0,0],
'f':[16,0,0,0,0,0,0,0],'fire':[16,0,0,0,0,0,0,0],'stop':[0,0,0,0,0,0,0,0],'up':[1,
0,0,0,0,0,0,0],'l':[4,0,0,0,0,0,0,0],'down':[2,0,0,0,0,0,0,0],'s':[0,0,0,0,0,0,0,0
],'r':[8,0,0,0,0,0,0,0],'u':[1,0,0,0,0,0,0,0],'left':[4,0,0,0,0,0,0,0]})]and[(lamb
da f,arg:f(arg,f))((lambda busses,foo:(lambda dev,busses:dev and[globals().__setit
em__("dev",dev[0])or globals().__setitem__("conf",dev[0].configurations[0])or glob
als().__setitem__("intf",conf.interfaces[0][0])or globals().__setitem__("endpoints
",[e.address for e in intf.endpoints])]or foo(busses[1:]))([d for d in busses[0].d
evices if d.idVendor==0x1941 and d.idProduct==0x8021],busses)),__import__("usb").b
usses())]and[globals().__setitem__("handle",dev.open())or handle.setConfiguration(
conf)or handle.claimInterface(intf)or handle.setAltInterface(intf)]and[globals()._
_setitem__("do",lambda command,time=0.0:[COMMAND.has_key(command)and handle.contro
lMsg(0x21,0x09,COMMAND[command],0x2,0x0)]and[time and[__import__("time").sleep(tim
e)or do("stop")]])]and[__name__=="__main__"and(lambda Command:[globals().__setitem
__(cmd,Command(cmd))for cmd in COMMAND]and globals().__setitem__("FIRE",Command("f
ire",6.0)))((lambda cmd,time=0.1:(lambda self:self.__dict__.__setitem__("__repr__"
,lambda:do(cmd,time)and"")or self)(__import__("HTMLParser").HTMLParser())))or[do(x
,3.0/8)or do("s",1.0/8)for x in"uduldudrudududus"]]
どう動くのかは下の動画をご覧下さい。

…音と画像が合っていないですね…。


追記:先ほど公開した上の記事には誤りがありました。 Hackathonで作ったコードは下のような物だったようです。 昨日新入社員歓迎会で飲み過ぎたため、 寝ている間に小人さんがいじったことに気がつかず、 そのまま公開してしまいました。 申し訳ありませんでした。

追記:SourceForge.net: PyUSBpyusbモジュールについて。を参考にしました。

# -*- coding: cp932 -*-
"""Interface to manupilate MissileLauncher"""

__all__ = ["MissileLauncher"]

from time import sleep
import usb

COMMAND = {}
def make(x):
    result = [0] * 8
    result[0] = x
    return result
    
COMMAND["left"] = make(0x4)
COMMAND["right"] = make(0x8)
COMMAND["up"] = make(0x1)
COMMAND["down"] = make(0x2)
COMMAND["fire"] = make(0x10)
COMMAND["stop"] = make(0x0)

short = {}
for k in COMMAND:
    short[k[0]] = COMMAND[k]

COMMAND.update(short)

class UsbDevice:
    def __init__(self, vendor_id, product_id):
        busses = usb.busses()
        self.handle = None
        for bus in busses:
            devices = bus.devices
            for dev in devices:
                if dev.idVendor == vendor_id and dev.idProduct == product_id:
                      self.dev = dev
                      self.conf = self.dev.configurations[0]
                      self.intf = self.conf.interfaces[0][0]
                      self.endpoints = [e.address for e in self.intf.endpoints]
                      return


        raise RuntimeError('Device not found.')

    def open(self):
        if self.handle:
            self.handle = None
        handle = self.dev.open()
        handle.setConfiguration(self.conf)
        handle.claimInterface(self.intf)
        handle.setAltInterface(self.intf)
        self.handle = handle


class MissileLauncher:
    def __init__(self):
        dev = UsbDevice(0x1941, 0x8021)
        dev.open()
        self.dev = dev
    def do(self, command, time = 0.0):
        if not COMMAND.has_key(command):
            return -1
        self.dev.handle.controlMsg(
            0x21,
            0x09,
            COMMAND[command],
            0x2,
            0x0)
        if time:
            sleep(time)
            self.do("stop")

def putCommandInGlobals():
    class Command(object):
        def __init__(self, cmd, time = 0.1):
            self.cmd = cmd
            self.time = time
        def __repr__(self):
            m.do(self.cmd, self.time)
            return ""

    for cmd in COMMAND:
        globals()[cmd] = Command(cmd)

    globals()["FIRE"] = Command("fire", 6.0)

def test():
    def do(a, t = 0.5):
        m.do(a, t * 0.75)
        m.do("stop", t * 0.25)
    [do(x) for x in "uduldudrudududus"]
        
if __name__ == "__main__":
    m = MissileLauncher()
    putCommandInGlobals()
    test()
    print "ok."

2007年04月16日

PythonでUDPブロードキャスト

ネットワークに関しては素人なので勉強中の西尾です。 UDPについて調べていたらDatagramという単語が出てきたので、 何だろうと思って調べてみたのですが、 そもそもUDPはUser Datagram Protocolの略でした。(´・ω・`)

UDP Broadcast Server : UDP : Network : Python examples (example source code) Organized by topic を見たところ、簡単に実装できそうだったので、実際に動かして勉強することにしました。 結果、サーバが25行、クライアントが27行になりました。

素直に書いてしまうとサーバから何も送られてこない時にクライアントが待機状態に入ってしまって ハングアップしたように見えてしまいます。 UNIXではノンブロッキングモードというそうならないモードがあるようですが、 Windowsでは使えないので、selectモジュールを使ってブロックされないようにしました。

あとクライアント側で、データが来たかどうかにかかわらず同じ長さsleepしていると、 サーバがデータを送る頻度が高いときにどんどん取り残されていくので、 データが来ていたときにはsleepしないですぐ「次のデータは来ているかな?」とチェックするようにしました。

見よう見まねのコードですが、作っていて自分がだいぶ勉強になったのでシェアしたいと思います。 「そこは違う!」などのつっこみは大歓迎します。

サーバのコードがこちら。

import socket, traceback
from time import sleep

host = ''   # Bind to all interfaces
port = 51423
address = "10.89.107."

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.bind((host, port))

count = 0
while True:
    try:
        s.sendto(str(count), (address + "255", port))
        print "sent", count
        sleep(0.8)
        count += 1
        
    except (KeyboardInterrupt, SystemExit):
        break
    except:
        traceback.print_exc()
        break

クライアントのコードがこちら。

import socket, traceback
from select import select
from time import sleep

host = ''   # Bind to all interfaces
port = 51423

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host, port))

while True:
    try:
        ins, outs, errs = select([s], [], [], 0)
        if ins:
            for i in ins:
                message, address = i.recvfrom(8192)
                print "message:", message, "from", address
        else:
            print "none..."
            sleep(1)
        
    except (KeyboardInterrupt, SystemExit):
        break
    except:
        traceback.print_exc()
        break

このサーバスクリプトを1台のマシンで走らせ、 クライアントを複数のマシンで走らせてみると、 きちんと一つのプログラムが送り出した情報を複数のプログラムが受け取っていることがわかります。 めでたしめでたし。


追記。 このあとJavaでもブロードキャストやマルチキャストのコードを書いたのですが、ブロードキャストで毎秒40メガバイトのパケットをまき散らしたせいでラボのネットワークが大変なことになってしまいました。いろいろ考えた結果、今回作ろうとしているものにはUDPブロードキャストを使わないことにしました。

2007年04月17日

PythonからUSBミサイルランチャを操作する短いサンプル

先日の記事(PythonワンライナーでUSBミサイルランチャーを制御)はネタに走りすぎていてわかりやすくはなかったので、 あらためて短いサンプルを書きました。

PythonからUSBミサイルランチャーを操作するためには、 libusb-win32とPyUSBというモジュールをインストールする必要があります。 pyusbモジュールについて。 というページでWindows版のインストーラが公開されているので、 それを使うと簡単にインストールできます。

インストールが完了して、USBミサイルランチャーを接続したら、 下の34行のスクリプトを実行すると 上下左右に移動した後発射されるはずです。

VENDER_ID = 0x1941
PRODUCT_ID = 0x8021

def find(vendor_id, product_id):
    import usb
    busses = usb.busses()
    for bus in busses:
        for dev in bus.devices:
            if dev.idVendor == vendor_id and dev.idProduct == product_id:
                return dev

    raise RuntimeError('Device not found.')

dev = find(VENDER_ID, PRODUCT_ID) 
conf = dev.configurations[0]
interface = conf.interfaces[0][0]

handle = dev.open()
handle.setConfiguration(conf)
handle.claimInterface(interface)
handle.setAltInterface(interface)

def do(command, time = 0.0):
    handle.controlMsg(0x21, 0x09, command, 0x2, 0x0)
    if time:
        from time import sleep
        sleep(time)
        do([0, 0, 0, 0, 0, 0, 0, 0]) # stop

do([1, 0, 0, 0, 0, 0, 0, 0], 1.0) # up
do([2, 0, 0, 0, 0, 0, 0, 0], 1.0) # down
do([4, 0, 0, 0, 0, 0, 0, 0], 1.0) # left
do([8, 0, 0, 0, 0, 0, 0, 0], 1.0) # right
do([16, 0, 0, 0, 0, 0, 0, 0], 6.0) # fire

2007年04月19日

Re: Python で改行を含む長い文字列をスマートに記述する

Python で改行を含む長い文字列をスマートに記述する ― TRIVIAL TECHNOLOGIES 2.0を読んで、 ちょっとした関数"multiline"を作ってみました。

下のコードを実行するとその下のように出力されます。

>>> multiline("""
[foo]
  [bar]
    [baz]
      hoge
    [/baz]
  [/bar]
[/foo]
""")
'\n'.join([
    '[foo]',
    '  [bar]',
    '    [baz]',
    '      hoge',
    '    [/baz]',
    '  [/bar]',
    '[/foo]',])

逆に、この出力の前にprintとつけて実行すると元に戻ります。

>>> print '\n'.join([
    '[foo]',
    '  [bar]',
    '    [baz]',
    '      hoge',
    '    [/baz]',
    '  [/bar]',
    '[/foo]',])
[foo]
  [bar]
    [baz]
      hoge
    [/baz]
  [/bar]
[/foo]

ソースは以下のようになります。たいしたことはしてません。

def multiline(s):
    s = s.strip("\n")
    print ("'\\n'.join([\n" +
            "\n".join("    %s," % `x`
                      for x in s.split("\n")) +
           "])")

個人的には全ての行末に\nがつくのと、 頭に"\n".joinがつくのでは後者の方がマシだと思っているのですが、 そうでない人には下のコードの方がいいかもしれません。

>>> def multiline(s):
    s = s.strip("\n")
    print "\n".join(
	    r"    %s\n'" % `x`[:-1]
	    for x in s.split("\n"))
これだと下のように出力されます。 Pythonでは隣接する文字列リテラルが結合されるので この複数行の状態のまま普通に使って問題なく動きます。
    
>>> multiline("""
[foo]
  [bar]
    [baz]
      hoge
    [/baz]
  [/bar]
[/foo]
""")
    '[foo]\n'
    '  [bar]\n'
    '    [baz]\n'
    '      hoge\n'
    '    [/baz]\n'
    '  [/bar]\n'
    '[/foo]\n'

最後になりましたが、 4.8 textwrap -- テキストの折り返しと詰め込みのdedentを使うというのも一つの手ですね。

About 2007年04月

2007年04月にブログ「西尾泰和のブログ @ Cybozu Labs」に投稿されたすべてのエントリーです。過去のものから新しいものへ順番に並んでいます。

次のアーカイブは2007年05月です。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。