Hatena::ブログ(Diary)

Learning to be Me

2009-03-16

[]デスクトップの背景を黒一色にする 02:59

地味にやり方をわすれてしまうのでメモ代わりに。

デスクトップの背景は黒一色がよいのだが、デフォルトで選択できるの「無地の色」の中にはそれがない。

f:id:bubbles:20090317025424p:image

ここにでてきているのは、/Library/Desktop Pictures/Solid Colors の中身っぽいので、


f:id:bubbles:20090317025656p:image

こんな感じの適当に黒い画像を作って*1、Library/Desktop Pictures/Solid Colors にいれてあげると、


f:id:bubbles:20090317025422p:image

上の画像のように、フォルダに入れた画像が選べるようになる。


f:id:bubbles:20090317030227p:image

ということで、自分のデスクトップはこんな感じ。真っ黒な背景はおちつく。

別に黒に限らず特定の色、一色にしたい場合はこれでいける。

*1:ちなみに自分は、Terminal.app の背景を黒にして、スクリーンショットをとって作った。

トラックバック - http://d.hatena.ne.jp/bubbles/20090316

2009-03-15

[]「日本語WordNetデータベースを探索するフロントエンドプログラム」を Ruby で書き直した 03:38

日本語WordNetのデータベースを探索するフロントエンドプログラム - yanbe.diff - subtech を読んで使ってみようとしたが、Python 2.6 が手元のマシンに入っておらず、apt で探すもみつからず、絶望的な気分になり、Ruby で書き直してみた。

基本的にはそのまま。usage とかもコピペ。嵌まった点としては、Python では空のリストが偽であるということ*1

#!/usr/bin/ruby -Ku
# -*- coding: utf-8 -*-

require 'rubygems'
require 'sqlite3'

class WNJpn

  Word = Struct.new("Word",:wordid, :lang, :lemma, :pron, :pos)
  Sense = Struct.new("Sense",:synset, :wordid, :lang, :rank, :lexid, :freq, :src)
  Synset = Struct.new("Synset",:synset, :pos, :name, :src)
  SynLink = Struct.new("SynLink",:synset1, :synset2, :link, :src)

  def initialize(dbfile)
    @conn = SQLite3::Database.new(dbfile)
  end

  def get_words(lemma)
    @conn.execute("select * from word where lemma=?",lemma).map{|row|Word.new(*row)}
  end

  def get_word(wordid)
    Word.new(*@conn.get_first_row("select * from word where wordid=?",wordid))
  end

  def get_senses(word)
    @conn.execute("select * from sense where wordid=?",(word.wordid)).map{|row|Sense.new(*row)}
  end

  def get_sense(synset, lang="jpn")
    row = @conn.get_first_row("select * from sense where synset=? and lang=?",synset,lang)
    row ? Sense.new(*row) : nil
  end

  def get_synset(synset)
    row = @conn.get_first_row("select * from synset where synset=?",synset)
    row ? Synset.new(*row) : nil
  end

  def get_syn_links(sense, link)
    @conn.execute("select * from synlink where synset1=? and link=?",sense.synset,link).map{|row|SynLink.new(*row)}
  end

  def get_sys_links_recursive(senses, link, lang="jpn", depth=0)
    senses.each do |sense|
      syn_links = get_syn_links(sense, link)
      puts "#{'  ' * depth}#{get_word(sense.wordid).lemma} #{get_synset(sense.synset).name}" unless syn_links.empty?
      _senses = syn_links.map{|syn_link|get_sense(syn_link.synset2,lang)}.compact
      get_sys_links_recursive(_senses, link, lang, depth + 1)
    end
  end

  def main(word, link, lang='jpn')
    if words = get_words(word)
      sense = get_senses(words.first)
      get_sys_links_recursive(sense, link, lang)
    else
      STDERR.puts "(nothing found)"
    end
  end

  def self.print_usage
    puts <<-EOS
usage: wn.rb word link [lang]
    word      word to investigate

    link      syns - Synonyms
      hype - Hypernyms
      inst - Instances
      hypo - Hyponym
      hasi - Has Instances      mero - Meronyms
      mmem - Meronyms --- Member
      msub - Meronyms --- Substance
      mprt - Meronyms --- Part
      holo - Holonyms
      hmem - Holonyms --- Member      hsub - Holonyms --- Substance      hprt - Holonyms -- Part      attr - Attributes
      sim - Similar to      entag - Entails
      causg - Causes
      dmncg - Domain --- Category
      dmnug - Domain --- usage      dmnrg - Domain --- Region
      dmtcg - In Domain --- Category      dmtug - In Domain --- usage      dmtrg - In Domain --- Region      antsg - Antonyms

    lang (default: jpn)
      jpn - Japanese
      eng - English
    EOS
  end

  def close_db
    @conn.close
  end

end


if __FILE__ == $0
  if ARGV.length >= 2
    dbfile = "wnjpn-0.9.db"
    wnj = WNJpn.new(dbfile)
    wnj.main(*ARGV)
    wnj.close_db
  else
    WNJpn.print_usage
  end
end

*1:Ruby では偽ではない。

2009-01-05

[]termtterで補完 17:57

list ユーザ名 とか、uri-open ってうつのがめんどくさいので、補完できるようにした。

require 'set'

class Termtter::Client

  public_storage[:users] ||= Set.new

  add_hook do |statuses, event, t|
    if !statuses.empty?
      case event
      when :update_friends_timeline, :replies, :list_user_timeline
        statuses.each do |s|
          public_storage[:users].add(s.user_screen_name)
          s.text.scan(/@[a-zA-Z_0-9]*/).each do |u| # reply
            public_storage[:users].add(u.gsub("@","")) unless u == "@"
          end
        end
      end
    end
  end

end

module Termtter
  module InputCompletor

    Commands = %w[exit help list pause update resume replies search show uri-open]

    CompletionProc = proc {|input|
      case input
      when /^l(ist)? +(.*)/
        username = $2
        if username.empty?
          Termtter::Client.public_storage[:users].to_a
        else
          Termtter::Client.public_storage[:users].to_a.
            grep(/^#{Regexp.quote username}/).map{|u| "list #{u}"}
        end
      when /^uri-open +(.*)/
        uri_open_com = $1
        if uri_open_com.empty?
          %w[clear list]
        else
          %w[clear list].
            grep(/^#{Regexp.quote uri_open_com}/).map{|c| "uri-open #{c}"}
        end
      else
        Commands.grep(/^#{Regexp.quote input}/)
      end
    }

  end
end

Readline.basic_word_break_characters= "\t\n\"\\'`><=;|&{("
Readline.completion_proc = Termtter::InputCompletor::CompletionProc

これを適当に保存して、.termtter から require すれば、補完できるようになる。

たとえば、

f:id:bubbles:20090105174916p:image

list ってうってタブ押すと、

f:id:bubbles:20090105174915p:image

ユーザ名が出てくるし、

f:id:bubbles:20090105174911p:image

そこで、適当にいれてタブ押すと

f:id:bubbles:20090105174910p:image

こんな感じで補完できる。

コマンドに関しても同様で

f:id:bubbles:20090105174909p:image

urまでうってタブ押せば、

f:id:bubbles:20090105174908p:image

こんな感じに補完される。

jugyojugyo 2009/01/05 18:03 おお!これは素晴らしいですね。
本体に取り込ませてもらっていいですか?

bubblesbubbles 2009/01/05 18:07 是非お願いします!

jugyojugyo 2009/01/05 18:52 本体に取り込みましたー。
require 'termtter/completion' すれば有効になります。

デフォルトで無効にしてるんですが、すごく便利な機能なので、
コマンド追加関連の処理をもうちょっと改良してから標準機能に(デフォルトで有効に)するつもりです。

ujihisaujihisa 2009/01/05 19:01 本体のコミットログみてここに飛んできました。
補完機能つけたいと思いつつもどうすればいいかなあと思っていたのです。ありがとうございます!
uで@をつけるときなども補完されてほしいので、そのあたり修正してコミットしてみたいなと思います。

ujihisaujihisa 2009/01/05 19:47 早速uにも対応させて、
http://github.com/jugyo/termtter/commit/9e936441ab0ef0f7c326c1e1bff3bf0e394f5d8b
ついでに整理してみました
http://github.com/jugyo/termtter/commit/d5238f2e8f42b1050a05035f70ec04bc562e1f61

bubblesbubbles 2009/01/05 20:17 >jugyo さん
ありがとうございます。
コマンドのリストどうやってとればいいかわかんなかったんで、直で書いてしまいました。
termtterすごい便利に使わせてもらってます。

>ujihisa さん
自分が@をあんまり使ってないんで、そこまで気がついてなかったです。
ありがとうございます。

トラックバック - http://d.hatena.ne.jp/bubbles/20090105

2009-01-02

[][]termtter から growl に通知する 22:38

404 Not Foundがおもしろそうなので、termtter から growl に通知してみた。

できていること

require 'meow'

Termtter::Client.add_hook do |statuses, event|
  if !statuses.empty? && event == :update_friends_timeline
    growl = Meow.new("Termtter")
    statuses.reverse.each do |s|
      screen_name = s.user_screen_name
      text = s.text.gsub("\n",'')
      growl.notify(screen_name,text)
    end
  end
end

これを好きなとこにおいて、.termtterから require すればOK*1。とりあえず、誰の発言かということとその内容はでる。

超簡単ですばらしいと思った。

よくわからないこと

現状よくわかんないのが、どうやってアイコンを出すかということで、

.termtterに、

module Termtter

  class Status
    %w(
      id text created_at truncated in_reply_to_status_id in_reply_to_user_id
      user_id user_name user_screen_name user_profile_image_url # ここに user_profile_image_url を追加した
    ).each do |attr|
      attr_accessor attr.to_sym
    end
  end

  class Client
    def get_timeline(uri, update_since_id = false)
      doc = Nokogiri::XML(open(uri, :http_basic_authentication => [@user_name, @password]))
      statuses = []
      doc.xpath('//status').each do |node|
        status = Status.new
        %w(
          id text created_at truncated in_reply_to_status_id in_reply_to_user_id
          user/id user/name user/screen_name user/profile_image_url  # ここにも user/profile_image_url を追加した
        ).each do |key|
          method = "#{key.gsub('/', '_')}=".to_sym
          status.send(method, node.xpath(key).text)
        end
        status.created_at = Time.utc(*ParseDate::parsedate(status.created_at)).localtime
        statuses << status
      end

      if update_since_id && !statuses.empty?
        @since_id = statuses[0].id
      end

      return statuses
    end
  end
end

とかを書いて、user_profile_image_url にもアクセスできるようにして

Termtter::Client.add_hook do |statuses, event|
  if !statuses.empty? && event == :update_friends_timeline
    growl = Meow.new("Termtter")
    statuses.reverse.each do |s|
      screen_name = s.user_screen_name
      text = s.text.gsub("\n",'')
      tmp = OSX::NSURL.URLWithString_(s.user_profile_image_url) #ここと
      icon = OSX::NSImage.alloc.initWithContentsOfURL(tmp) #ここと
      growl.notify(screen_name,text,:icon=>icon) #ここを追加
    end
  end
end

というようにしてあげれば、いいのかなーと思ったのだけれど、OSX::NSURL.URLWithString_(s.user_profile_image_url)がとれるのもあったり、 nil になったりするのがあって、よくわからなくなっている。あとでちゃんと考えたい。

[][] termtter から growl に通知する の続き 02:45

引き続きtwitter、というかtermtterの話。

termtter から growl に通知する - Learning to be Me の続き。growlで通知するとこまではできたのだが、ユーザのアイコンが表示できていなかった。url指定しても、うまくいったり行かなかったりなので、結局一度ダウンロードして、画像を表示することにした。

~/sketch/ruby/growl-send.rb とかそんな感じの適当なファイルに、

require 'meow'
require 'uri'

def get_icon(s)
  cache_file = "#{Termtter::CACHE_DIR}/#{s.user_id}"
  unless File.exist? cache_file
    buf = ""
    open(URI.encode(s.user_profile_image_url)) do |r|
      buf = r.read
    end
    open(cache_file,"w") do |r|
      r.write(buf)
    end
  end
  return OSX::NSImage.alloc.initWithContentsOfFile(cache_file)
end

Termtter::Client.add_hook do |statuses, event|
  if !statuses.empty? && event == :update_friends_timeline
    growl = Meow.new("Termtter")
    statuses.reverse.each do |s|
      screen_name = s.user_screen_name
      text = s.text.gsub("\n",'')
      icon = get_icon(s)
      growl.notify(screen_name,text,:icon=>icon)
    end
  end
end

を保存してあげて、

.termtter を、

module Termtter

  CACHE_DIR = '/tmp/termtter-icon-cache-dir'

  class Status
    %w(
      id text created_at truncated in_reply_to_status_id in_reply_to_user_id
      user_id user_name user_screen_name user_profile_image_url
    ).each do |attr|
      attr_accessor attr.to_sym
    end
  end

  class Client
    def get_timeline(uri, update_since_id = false)
      doc = Nokogiri::XML(open(uri, :http_basic_authentication => [@user_name, @password]))
      statuses = []
      doc.xpath('//status').each do |node|
        status = Status.new
        %w(
          id text created_at truncated in_reply_to_status_id in_reply_to_user_id
          user/id user/name user/screen_name user/profile_image_url
        ).each do |key|
          method = "#{key.gsub('/', '_')}=".to_sym
          status.send(method, node.xpath(key).text)
        end
        status.created_at = Time.utc(*ParseDate::parsedate(status.created_at)).localtime
        statuses << status
      end

      if update_since_id && !statuses.empty?
        @since_id = statuses[0].id
      end

      return statuses
    end

  end

end

Dir.mkdir(Termtter::CACHE_DIR) unless File.exist?(Termtter::CACHE_DIR)
require File.expand_path('~/sketch/ruby/growl-send.rb')
configatron.user_name = 'ユーザ名'
configatron.password = 'パスワード'

という感じにしてあげれば、アイコン付きで、termtterからgrowlに通知がいく、はず。

*1:ただし、meow必須

jugyojugyo 2009/01/03 21:09 termtter を使っていただいてありがとうございます!
user_profile_image_url を取得できるようにプログラムを修正しました。
よかったらアップデートしていただければと思います。

bubblesbubbles 2009/01/04 17:40 おおお。
ありがとうございます!
アップデートしました。

2008-12-22

[] debian etchsubversion のバージョンをあげる 04:31

研究室で計算用サーバとして使ってるマシーン(debian etch)で

% svn up

ってやると、「svn: このクライアントは、作業コピー '.' を扱うには古すぎます。もっと新しい Subversion クライアントをダウンロードしてください。」などと言われた。

ググると subversion が古いからこうなるらしい。

ということで、新しくした手続きのメモ。といっても、backportsから持ってくるだけ。

/etc/apt/preferencesに

Package: *
Pin: release a=etch-backports
Pin-Priority: 1

Package: *
Pin: release a=stable
Pin-Priority: 800

Package: subversion
Pin: release a=etch-backports
Pin-Priority: 900

Package: libsvn1
Pin: release a=etch-backports
Pin-Priority: 900

という感じでかいてあげて、

% sudo apt-get update
% sudo apt-get dist-upgrade

としてあげればOK

トラックバック - http://d.hatena.ne.jp/bubbles/20081222