coLinux で Emacs の kill-ring の内容をWindowsのクリップボードと同期する とか coLinux 上の Emacs の kill-ring の内容をWindowsのクリップボードと同期する by Perl から。

上記のサイトのをもってきて.vimrcをちょろっと書けばいいんだけど、テキストを受信するサーバーは常時立ち上げていそうで、スクリプトだとなんとなくアレなので、exeがいいなあ。簡単にできそうなので、とりあえず探すよりは作ってみるメソッド。

ファイル一式

clsync.zip

内容は

  • clipboard_server.exe:Windows側で実行するサーバー
  • clipboard_server.ini:サーバーのポートの設定
  • clsync.py:vmware側におくクライアントスクリプト

使い方など

Windows側ではclipboard_server.iniで適当にポートを設定して(当然外部から見えないやつを)、clipboard_server.exeを起動しておいてください。タスクトレイにアイコンが表示されてますので、そこから終了できます。

vmware上のLinuxですが、まずnkfがインストールされている必要があります。すみません、クライアント側手抜きするために使っています。そしてclsync.pyをパスの通ったところにおきます。clsync.pyにはホストのWindows側で動いてるサーバーのポートとIPを設定する箇所があるので、適宜書き換えます。またポートとIPはオプションでも渡せます(詳しくはclsync.py -hで)。

あとは

function! SyncClipboard()
  let cliptext = substitute(getreg(), "'", "'\"'\"'", 'g')
  let cmd = "echo '" . cliptext . "' | clsync.py"
  call system(cmd)
endfunction
nmap ,c :call SyncClipboard()<CR>

こんな感じなのを.vimrcに追加すればOK。最後にyankしたものが,cでホストのWindowsのクリップボードに送られます。

追記: っとよく考えたらvmw copyとまったく一緒。.vimrcのclsync.pyの部分をvmw copyに置き換えれば同じように動作する。ただ、vmwの説明には

コピーするテキストに改行コード以外の非 ASCII 文字を含めることはできません。 ゲストおよびホスト OS によっては、キャリッジリターンの付与・除去が行われ ます。 コピー可能なテキストの最大長は約 65,000 バイトです。

ってある。 今回作ったモノの場合はnkfをかませているのでもちろんいつでも日本語オーケー。 それ以上は深くみてないけど、UTF-8だと日本語でもvmw copyしても問題なさそうだ。EUC-JPの「ほげほげ」とかいう内容のtest.txtをvmw copy test.txtとかするとダメだった。

ま、というわけで、 cat test.txt | nkf -w | vmw copyで万事解決なんだけど。

というわけでこのツールは使われないまま終わる(笑 coLinuxはよく知らないけど、coLinuxにこういうホストOSへのクリップボードコピーコマンドがないなら、coLinuxでは役に立つかも。


以前書いてみたアクセサ をpropertyを使って改良してみる。

propertyというのはnew-styleクラスに対してのみ使える、メソッドで

class C(object):
    def __init__(self): self.__x = None
    def getx(self): return self.__x
    def setx(self, value): self.__x = value
    def delx(self): del self.__x
    x = property(getx, setx, delx, "I'm the 'x' property.")

こんな感じで使う。

Rubyを使った人なら分かると思うけど、プロパティ名でのアクセス=アクセサ呼び出しになるのは結構便利。 ActiveRecordなんかでは、特に便利だと感じる。あ、このプロパティ、もういっそエンコードされた値を返したい、と思ったときにそのクラスにプロパティ名と同じメソッドを定義するだけでいいし。

前につくったアクセサは「get_hoge()」や「set_hoge()」といった感じで呼び出されたけど、これにpropertyを使うことによってRubyと同じような属性アクセスが可能になる。

とりあえずこんな感じ。

#!/usr/bin/python
# vim: fileencoding=utf-8
from itertools import * 

def property_accessor(cls, *names): 
  _property(cls, names)

def property_reader(cls, *names):
  _property(cls, names, True)

def property_writer(cls, *names):
  _property(cls, names, False, True)

def _property(cls, names, error_setter = False, error_getter = False):
  for attr_name, doc in _name_and_docs(names):
    real_name = _real_name(cls, attr_name)
    setter  = _add_setter(cls, attr_name, real_name, error_setter)
    getter  = _add_getter(cls, attr_name, real_name, error_getter)
    deleter = _add_deleter(cls, attr_name, real_name)
    setattr(cls, attr_name, property(getter, setter, deleter, doc))

def _name_and_docs(name_or_tuples):
  return imap(_name_and_doc ,name_or_tuples)

def _name_and_doc(name_or_tupple):
  if isinstance(name_or_tupple, ("".__class__, u"".__class__)) :
    return (name_or_tupple, "")
  else:
    return name_or_tupple

def _real_name(cls, name) :
  return "_%s__%s"%(cls.__name__, name)

def _add_method(cls, verb, name, method_to_add):
  method_name = "%s_%s" % (verb, name)
  method = cls.__dict__.get(method_name)
  if not method :
    method = method_to_add
    setattr(cls, method_name, method)
  return method

def _add_setter(cls, name, real_name, error = False) :
  if error:
    def setter(self, v):
      raise AttributeError, "class %s: %s is a read-only attribute." % (cls.__name__, name)
  else:
    setter = lambda self, v : setattr(self, real_name, v)
  return _add_method(cls, "set", name, setter)

def _add_getter(cls, name, real_name, error = False) :
  if error:
    def getter(self):
      raise AttributeError, "class %s: %s is a write-only attribute." % (cls.__name__, name)
  else:
    getter = lambda self : getattr(self, real_name)
  return _add_method(cls, "get", name, getter)

def _add_deleter(cls, name, real_name) :
  deleter = lambda self : delattr(self, real_name)
  return _add_method(cls, "delete", name, deleter)

class AccessorType(type):
  def __new__(cls, class_name, class_bases, classdict):
    cls = type.__new__(cls, class_name, class_bases, classdict)
    list = ["__accessor__", "__reader__", "__writer__"]
    methods = imap(lambda n: eval("property_%s"%n.strip("_")), list)
    for accessor_type, method in izip(list, methods):
        if classdict.has_key(accessor_type):
          method(cls, *classdict[accessor_type])
    return cls

class Accessor(object):
  __metaclass__ = AccessorType
  def __init__(self, *args) :
    list = ["__accessor__", "__reader__", "__writer__"]
    for accessor_type in list:
      if not self.__class__.__dict__.has_key(accessor_type) : continue
      for name in self.__class__.__dict__.get(accessor_type):
        attr_name, doc = _name_and_doc(name)
        if not self.__dict__.get(attr_name):
          exec("self._%s__%s = None" % (self.__class__.__name__, attr_name))

使い方は

#!/usr/bin/python
# vim: fenc=utf-8
import accessor

class Test(accessor.Accessor) :
  __accessor__ = [("x", "This is x value"), "y", "z"]
  __reader__    = ["r"]

  def __init__(self):
    accessor.Accessor.__init__(self)

  def get_y(self) :
    return "new y value"

obj = Test()
obj.x = "hoge"

print obj.x
# => hoge

print Test.x.__doc__
# => This is x value

print obj.y
# => new y value

obj.r = "value"
# => AttributeError: class Test: r is a read-only attribute.

こんな感じ。

get_* とか set_* を定義すれば、独自のアクセサを定義できる。 属性は全て private(__*) として保存されているので、 self.__x みたいな感じでクラス内ではアクセスできる。

これやっててはまったのはココ。

exec("self._%s__%s = None" % (self.__class__.__name__, attr_name))

いや self.__dict__[attr_name] = None でいいんだけど、ふとexecを使うと予想外なことがおこったのであえて記事にのっけてみた。

なんとこれじゃだめなのだ!!ふつうPythonでは self.__x = hoge とすると自動的に内部で self.__dict__["_classname__x"] = "hoge" に変換される。

でもexecでやるとどうやらこの過程をぶっ飛ばしてしまうらしい。要はprivateになってくれない。なので自分で変換してやることで解決する。

ところでこういうアクセサ生成メタクラスってネットにいっぱい転がってるんだけど、そろそろ標準で組み込まれたりしないんだろうかねえ。


Pythonのコードをwindows用exeにするには当然、 py2exe を使います。

で、そのとき、

python setup.py py2exe --windows

とすれば、コンソールを表示しないようにできます。

これはもう古いやり方で、現在のバージョンのpy2exeでは動きません・・・。現在はターゲットファイルの拡張子を.pywにする、もしくは windows = [{'script' : 'script.py', "icon_resources": [(1,"script.ico")]}] というオプションをsetupに渡す、という方法になっています。

今日、昔py2exeで作ったファイルが出てきたんですが、困ったことにpythonのソースファイルはない。いや、別に改良とかもうしないしいいんだけど。 ふと起動してみるとGUI with console。かっちょわりい。

コンソールが表示される、というのは単純にPEファイルのオプションなわけで。

image

この部分を変更してやればとりあえずは直る。ちなみに、

  • 00 00:未知のサブシステム
  • 01 00:デバイス ライバおよびWindowsNTネイティブプロセス用
  • 02 00:GUIで実行されるファイル
  • 03 00:コンソールで実行されるファイル
  • 07 00:Posixコンソールで実行されるファイル
  • 09 00:WindowsCEで実行されるファイル

なので02h 00hにしてやればオッケー。

Pythonでバイナリファイルの読み書きとかしたことないので練習もかねて。

# vim: fileencoding=utf-8
import sys
from struct import * 
target_file = len(sys.argv) > 1 and sys.argv[1] or sys.exit("Target file is not specified.")
target_file = unicode(target_file, "mbcs")
out_file    = open(target_file + "_gui", "wb")

io = open(target_file, "rb")
while 1 :
  if io.read(1) == "P" and io.read(1) == "E" : break
subsystem_pos = io.tell() +90 
io.seek(0)
out_file.write(io.read(subsystem_pos))
io.seek(4, 1)
out_file.write(pack('hh', 2, 0))
out_file.write(io.read())

io.close()
out_file.close()

こんな感じ。引数にexeファイルを渡せばオッケー。でも、適当なので全部のケースで動くかはあやしいw pythonではバイナリを扱うときはstructモジュールを使う、ということが分かりました(笑