デスクトップの背景を黒一色にする

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

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

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


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


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


ということで、自分のデスクトップはこんな感じ。真っ黒な背景はおちつく。
別に黒に限らず特定の色、一色にしたい場合はこれでいける。

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

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

日本語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 では偽ではない。

termtterで補完

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 すれば、補完できるようになる。

たとえば、

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

ユーザ名が出てくるし、

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

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

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

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

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

termtter から growl に通知する

http://jugyo.org/blog/2931がおもしろそうなので、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 になったりするのがあって、よくわからなくなっている。あとでちゃんと考えたい。

*1:ただし、meow必須

termtter から growl に通知する の続き

引き続き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に通知がいく、はず。

debian etch の subversion のバージョンをあげる

研究室で計算用サーバとして使ってるマシーン(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

grep --color について

find/grep/xargsコマンドを使いこなす 業務で楽するためのUNIXテクニック集「検索」編 (1/4):CodeZine(コードジン) をみて思い出したけれど、
grep に --color オプションつけなくても、

export GREP_OPTIONS='--color=always'

って .zshrc なり .bashrc にかいておくと、常に grep の結果に色がつく。

でも、grep をパイプでつないでいるときに、色が邪魔してうまく動かなかったりする*1

そのときは、スクリプト内の grep に --color=never というオプションを渡してあげれば、色をつかなくできる。

*1:例えば、ubuntu 8.10 で、netbeans 6.5 の起動がうまくいかなかったり。