JythonでつくるTwitterクライアント

プログラミング演習という授業で、swingを使ってGUIアプリを作れという課題が出されたところで、


「swing(とEventListener)を使ってさえいれば良いんだよね?」


というへりくつを思いつき、Jythonを使ってみたくなりました。
しかもちょうど最近2.5.0が出たばかりととてもいいタイミングです。

Jythonって何だ

もっともよく使われているPythonはCで書かれています。そこをCじゃなくてJavaで作ったPythonJythonです。
JythonではPythonのコードはJavaバイトコードに変換されて処理されるので、JavaVM上で動くプログラムがPythonで書ける、ということになります。

JythonTwitterClient


イベントリスナーの書き方くらいしかJavaとの違いはないですが、JavaのライブラリもPythonのモジュールも使えるという点はとても面白いです。


pythonのモジュールをJythonにインストールしたい場合は

jython setup.py install


でできます。かんたんですね。

'''
Created on 2009/06/22

@author: chkfj
'''
from javax.swing import *
from javax.swing.table import *
from javax.swing.event import *
from java.awt import *

import twitter,datetime,time

from settings import twitter_account

class TimelineSelectionListener(ListSelectionListener):
    def __init__(self,frame):
        self.frame=frame
    def valueChanged(self,event):
        self.frame.status_selected(event)

class TwitterClientFrame(JFrame):
    def __init__(self):
        self.api = twitter.Api(username=twitter_account.username, password=twitter_account.password)
        self.since = 0
        self.since_reply = 0
        
        self.setSize(480,640)
        self.setTitle("JythonTwitterClient - "+twitter_account.username)
        self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
        self.setResizable(False)
        self.setLayout(FlowLayout())
        
        self.dm=DefaultTableModel([], ("ScreenName","Text","Date",))
        self.t = JTable(self.dm)
        self.t.setAutoCreateRowSorter(True)
        self.t.getSelectionModel().addListSelectionListener(TimelineSelectionListener(self))
        
        self.dm_replies=DefaultTableModel([], ("ScreenName", "Text", "Date",))
        self.replies_table = JTable(self.dm_replies)
        self.replies_table.setAutoCreateRowSorter(True)
        

        self.input = JTextField(42)
        self.add(self.input)  
        self.button = JButton("Post",actionPerformed=self.post_and_update)
        self.add(self.button)
        
        self.statusview = JTextArea("Twitter",3,42)
        self.statusview.setLineWrap(True)
        statuspanel = JPanel()
        statuspanel.setSize(Dimension(self.width,40))
        statuspanel.add(self.statusview)
        self.add(statuspanel)
        
        self.tab = JTabbedPane(JTabbedPane.TOP)
        
        s_ft=JScrollPane(self.t)
        s_ft.setPreferredSize(Dimension(self.width-10,500))
        s_replies=JScrollPane(self.replies_table)
        s_replies.setPreferredSize(Dimension(self.width-10, 500))
        self.tab.addTab("Friends", s_ft)
        self.tab.addTab("Replies", s_replies)
        self.add(self.tab)
        self.show()
        
    def update_table(self):
        try:
            timeline = self.api.GetFriendsTimeline()
            timeline.reverse()
        except:
            timeline=[]
        try:
            replies = self.api.GetReplies()
            replies.reverse()
        except:
            replies = []
            
        for t in timeline:
            if self.since >= t.id:continue
            self.dm.addRow([t.user.screen_name, t.text, datetime.datetime(*time.localtime(t.created_at_in_seconds)[:-3]).isoformat().replace(datetime.datetime.now().strftime("%Y-%m-%dT"),"") ])
        for r in replies:
            if self.since_reply >= r.id:continue
            self.dm_replies.addRow([r.user.screen_name, r.text, datetime.datetime(*time.localtime(r.created_at_in_seconds)[:-3]).isoformat().replace(datetime.datetime.now().strftime("%Y-%m-%dT"),"") ])
        self.since=long(timeline[-1].id)
        self.since_reply=long(replies[-1].id)
    
    def post_and_update(self,event):
        if self.input.text:
            self.api.PostUpdate(self.input.text)
            self.input.text=""
        self.update_table()
    
    def status_selected(self,event):
        index=self.t.getSelectedRow()
        self.statusview.text = self.t.getValueAt(index, 0)+" : "+self.t.getValueAt(index,1)

def main():
    f=TwitterClientFrame()
    f.update_table()
    
if __name__=='__main__':
    main()

Ubuntuにeclipseを入れた話

最近は授業でeclipseを使う機会があり、Ubuntueclipsetomcatを入れる方法をいろいろと調べて何とか使っているので、気づいたことをメモしておきます。

aptで入るeclipseは3.2です

Ubuntu8.10で

sudo apt-get install eclipse eclipse-common-nlp

とかやるといい感じにEclipseが日本語化までされててハッピーな気持ちになったのですが、気のせいでした。この方法で入るeclipseのバージョンは3.2で、3.2だとMercurialEclipseが動きません。
最新版を使うには、eclipse.orgからアーカイブを落としてくることになります。

http://www.eclipse.org/downloads/
からEclipse IDE for Java EE Developers (163 MB)というやつのLinux 32bit版をダウンロードしました。

しかし僕はメニューが英語で書かれたソフトウェアなどろくに使ったことがありませんから、日本語化します。

Pleiadesの1.3.0というやつを落としてきて、eclipseのインストールディレクトリに上書き後、eclipse.iniの末尾に

 -javaagent:/home/username/eclipse/plugins/jp.sourceforge.mergedoc.pleiades/pleiades.jar


を追加します。

SunのJDKを使うように設定する

デフォルトだとgcjとかいうのになっててeclipseの起動がくそ遅いので、sun-java6-jdkを使うように設定します。やり方は


http://ubuntuforums.org/showpost.php?s=8980314367db7fa580b9191a145cd76f&p=1065271&postcount=8


に書いてある通りで、

sudo update-alternatives --config java


をやった後、/etc/jvmと/etc/eclipse/java_homeを編集します。

tomcatのインストール

sudo apt-get install tomcat5.5

で入れました。とりあえず演習では使えています。

MercurialEclipseのインストール

http://www.vectrace.com/mercurialeclipse/

ここの通りにインストールして、
プロジェクト右クリック→チーム→プロジェクトの共用
でMercurialEclipseを選択、そうするとhgのコマンドがGUIで使えるようになります。

PyDevのインストール

Python書くならVimなのは定説ですが、PyDevはそれでもGUIがいい人向けにおすすめできる開発環境です。


ヘルプのソフトウェア更新→サイトの追加
http://pydev.sourceforge.net/updates/


からインストールして、インタプリタやらの設定をするとPythonJythonも可)のコードが実行できるようになります。Djangoならmanage.pyをrunserverの引数付きで実行すればサーバ起動したりなんかもeclipseからできます。

GoogleAppEngineで全文検索

datastoreしかないのに全文検索とかどうやったらいいんだYO、という話ですが、
日本語に対応したSearchableModelがbitbucketで公開されています。
http://www.ianlewis.org/jp/gae-hackathon-disc-3-jp
http://bitbucket.org/a2c/gaehackathon_misopotato/


しかしこのSearchableModel、NgramSegmenterをimportしているのに実は使ってないという罠があるので注意が必要です。

やってみた

http://chkfj-gae.appspot.com/manifesto/


bi-gramで分けて、インデックスに突っ込んでいます。
1000件ちょっとなのにローカルでテストするとくそ重くて一時はどうしたものかと思いましたが、
アップロードしたらだいぶマシになったのでよかった。


あと今のところ関係ないですが、GAEではGoogleのアカウントで認証する仕組みが簡単に使えるのでそれを使ってます。

twopyを使ってみた

2chからスレッドやレスのデータを取得するために、以前は、

http://d.hatena.ne.jp/kokoromo/20080822/1219352356

というのを使っていましたが、吐き出されるJSONは変な場所にダブルクォーテーションが混じっていることがあって面倒くさいです。あと何かよく分からないけど取得が遅いような気もします。そこでtwopyというPython用の2chライブラリを知ったので使ってみました。

twopyの使い方は
映像奮闘記: Pythonistaのための2chライブラリ"twopy"
にある通り。

>>> from twopy import *
>>> board = Board("http://live24.2ch.net/eq/")
>>> board.retrieve()
200
>>> thread = [t for t in board if "あれって地震雲だよね?" in t.title][0]
>>> print u"%s (%i)" % (thread.title, thread.res)
【画像必須】あれって地震雲だよね?72【予言無用】 (943)
>>> thread.retrieve()
200
>>> res = [r for r in thread][940:]
>>> for r in res: print r.render()
... 
941 名前:M7.74(京都府) [sage]: 2009/07/27(月) 14:03:34.73 id:jMuYujB4
>>940
「かなとこ雲」で調べてみよう
夏に普通に見られます

ブォケ!と言わずに優しく書いてみました。

942 名前:M7.74(東海・関東) []: 2009/07/27(月) 14:11:32.08 id:BBewJzGj
>>940
5時前・埼玉・方角ワカラン

943 名前:M7.74(東海・関東) [sage]: 2009/07/27(月) 14:13:06.05 id:BBewJzGj
>>941 ありがとうorz
しかもあげちゃったぜ!!


Threadの__getitem__がsliceの受け渡しに対応していなかったので、レスを途中から取りたい場合はthread.pyを書き換えるか、一旦リストに入れてからスライスすると良いです。

音が鳴らなくなった時の対処(pulseaudio)

起動してしばらく使っていると音が鳴らなくなることがあって、いままでは音楽が聞きたくなったら再起動してたのだけど、面倒くさいので対処することにした。
調べてみるとpulseaudioとかいうやつが糞だと各所で書かれていて、どうやら自分のケースでもこれが悪さをしていたらしい。

http://youcharmanums.blog2.fc2.com/blog-entry-754.html
http://youcharmanums.blog2.fc2.com/blog-entry-754.html

ここの通りにpulseaudioをアンインストールした。
あと、

$ ps ax | grep audio
 6514 ?        Ssl   16:04 /usr/bin/pulseaudio -D --log-target=syslog
 6519 ?        S      0:00 /usr/lib/pulseaudio/pulse/gconf-helper
15418 pts/0    R+     0:00 grep audio
$ sudo kill -kill 6514

みたいにpulseaudioをkillしたら、再起動しなくても音が鳴った。ヤッター