Hatena::ブログ(Diary)

Koonies/こりゃいいな! このページをアンテナに追加 RSSフィード

2009 | 06 | 07 | 08 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 10 |
2011 | 01 | 02 | 05 | 06 | 07 | 08 | 09 |
2012 | 05 | 06 |

2011-09-06

keyhacでCapsLockをモディファイアキー定義して無理矢理使う

keyhacの話。ある2chのスレを見ていたら、keyhacでCtrlとCapsLockキーを入れ替えようとしたが無理だった、というやりとりを見かけました。

CapsLockというキーは他のキーとは違いDownのイベントは発生するけど、Upは発生しない(ドライバマスクしているらしい)ため、Ctrlと入れ替えた場合、押されっぱなしの状態になるとか。

試しにkeyhacで内部ログON*1にして、CtrlとCapsキーを押してみると

# Ctrlを押して離した場合

IN  : D-LCtrl
TRU : D-LCtrl
IN  : U-LCtrl
TRU : U-LCtrl
IN  : O-LCtrl

# CapsLockを押して離した場合

IN  : D-(240)
TRU : D-(240)

という風にCapsLockは「U-(240)」が出力されていません。だから入れ替えは無理、か。確かにこれは無理だなと思う反面、出来ないと言われると、何とかしたくなるのが人情

で、色々考えた末、入れ替えは無理でもユーザー定義モディファイアキー*2としてなら使えるかもと思い、書いてみました。

コード

# config.py

from keyhac import *


def configure(keymap):

    ## 関数実行時にモディファイアの状態をリセットするデコレータ
    def reset_modifier(func):
        import functools

        @functools.wraps(func)
        def _reset_modifier(*args, **kw):
            # モディファイアの状態を無理矢理リセット
            keymap.modifier = 0
            # 関数実行
            return func(*args, **kw)
        return _reset_modifier

    ## JobQueue/JobItem でサブスレッド処理にするデコレータ
    def job_queue(func):
        import functools

        @functools.wraps(func)
        def _job_queue(*args, **kw):

            num_items = JobQueue.defaultQueue().numItems()
            if num_items:   # 処理待ちアイテムがある場合は、その数を表示
                print u"JobQueue.defaultQueue().numItems() :", num_items

            def __job_queue_1(job_item):
                return func(*args, **kw)

            def __job_queue_2(job_item):
                # print "job_queue : ", func.__name__, args, kw
                pass

            job_item = JobItem(__job_queue_1, __job_queue_2)
            JobQueue.defaultQueue().enqueue(job_item)

        return _job_queue

    ## 一定時間経過後にモディファイアの状態をリセット
    @job_queue
    def auto_reset_modifier():
        import time
        WAIT_TIME = 0.5  # 秒
        time.sleep(WAIT_TIME)
        # モディファイアの状態を無理矢理リセット
        # if keymap.modifier: print keymap.modifier
        keymap.modifier = 0

    @reset_modifier
    def minimize():
        keymap.getTopLevelWindow().minimize()

    # どのウインドウにフォーカスがあっても効くキーマップ
    keymap_global = keymap.defineWindowKeymap()

    # ユーザモディファイアキーの定義:CapsLock(240) --> U1
    keymap.defineModifier("(240)", "U1")

    # 単体押しは一定時間後にモディファイアの状態をリセット
    # keymap_global["U1"] = auto_reset_modifier     # これだとキー表記エラー
    keymap_global["(240)"] = auto_reset_modifier

    # Caps + a : ウィンドウ最小化
    keymap_global["U1-a"] = minimize

関数定義で@reset_modifierのデコレータを使うと、関数の実行時にモディファイアキーの状態をリセットします。これによりCapsLockキーの押されっぱなしを防止しています。

これで一応モディファイアっぽく使えます。ただしタイトルに「無理矢理」と書いたように同時押しでなくても

CapsLockを押す → 離す → (0.5秒以内に)Aを押す

でも認識されてしまいます。また、CapsLockを押したままA、Aと2回押すと、2回目のAは同時押しではないAになってしまう等の欠点があります。

■ さいごに

日頃からCapsLockキーって押しやすい場所にあるのに全然使ってないなと思っている方はどうぞ。

■ 参考リンク

ページが見つかりませんでした - craftware

キーカスタマイズソフト 2

*1:トレイアイコン右クリック→内部ログONをクリック

*2:Ctrl・Shift・Alt・Winキーのような装飾キー

2011-08-19

keyhacでWindows7のエアロスナップをマネマネ。

DWindows7から導入されたエアロスナップ。要は画面左・右半分の最大化がマウス操作で簡単に実行できる機能です。ご存知ない方はこの30秒の動画を見てください。すごく便利な機能なんですが、僕の普段使いのPCXP。当然エアロスナップなんてありません。

だったらkeyhacの出番です。本家にない上半分・下半分も出来ちゃいます。ついでに移動、リサイズも紹介しときます。

コード

from keyhac import *

## 関数を返す関数にするデコレータ
def ret_func(func):
    import functools

    @functools.wraps(func)
    def _ret_func(*args, **kw):
        @functools.wraps(func)
        def __ret_func():
            return func(*args, **kw)
        return __ret_func
    return _ret_func

def configure(keymap):

    ## 10pixel単位のウインドウのリサイズ(最大最小のときは何もしない)
    #
    @ret_func
    def resize_window(direction):
        if not keymap.getTopLevelWindow().isMaximized():
            i = "LRUD".find(direction)
            dx, dy = ((-10, 0),  # L
                      (+10, 0),  # R
                      (0, -10),  # U
                      (0, +10)   # D
                     )[i]

            wnd = keymap.getTopLevelWindow()
            rect = list(wnd.getRect())
            rect[2] += dx
            rect[3] += dy
            wnd.setRect(rect)

    ## 10pixel単位のウインドウの移動(最大最小のときは何もしない)
    #
    @ret_func
    def move_window(direction):
        if not keymap.getTopLevelWindow().isMaximized():
            i = "LRUD".find(direction)
            dx, dy = ((-10, 0),  # L
                      (+10, 0),  # R
                      (0, -10),  # U
                      (0, +10)   # D
                     )[i]

            # keyhac_keymap.py 定義の関数を利用
            keymap.command_MoveWindow(dx, dy)()

    ## ウィンドウを画面端まで移動(最大最小のときは何もしない)
    #
    @ret_func
    def move_window_monitoredge(direction):
        if not keymap.getTopLevelWindow().isMaximized():
            i = "LURD".find(direction)
            # keyhac_keymap.py 定義の関数を利用
            keymap.command_MoveWindow_MonitorEdge(i)()

    ## 画面半分の切り替え(Windows7のエアロスナップ風)
    #
    @ret_func
    def toggle_aero_snap(direction):
        wnd = keymap.getTopLevelWindow()
        if wnd.isMaximized():
            # 元に戻す
            wnd.restore()
        else:
            # 最大化
            wnd.maximize()

            # ウィンドウのサイズを取得
            rect = list(wnd.getRect())

            # ウィンドウの幅/高さの半分
            width_50 = (rect[0] + rect[2]) / 2
            height50 = (rect[1] + rect[3]) / 2

            # 入力された方向に応じて対応する座標を幅/高さの半分で上書き
            i = "RDLU".find(direction)
            rect[i] = (width_50,  # R
                       height50,  # D
                       width_50,  # L
                       height50   # U
                      )[i]

            # ウィンドウの新しいサイズを反映
            wnd.setRect(rect)

    # どのウインドウにフォーカスがあっても効くキーマップ
    keymap_global = keymap.defineWindowKeymap()

    ## ウィンドウ移動系
    for arrow_key in ("Left", "Right", "Up", "Down"):
        c = arrow_key[:1]

        # Win+←→↑↓ : 10pixel単位のウインドウの移動
        keymap_global["W-" + arrow_key] = move_window(c)
        # Win+Ctrl+←→↑↓ : 画面の端まで移動
        keymap_global["W-C-" + arrow_key] = move_window_monitoredge(c)
        # Win+Shift+←→↑↓ : 10pixel単位のウインドウのリサイズ
        keymap_global["W-S-" + arrow_key] = resize_window(c)
        # Win+Alt+←→↑↓ : Windows7のエアロスナップ風
        keymap_global["W-A-" + arrow_key] = toggle_aero_snap(c)

■ 軽〜く説明

Win+←→↑↓10pixel単位ウインドウの移動
Win+Ctrl+←→↑↓画面の端まで移動
Win+Shift+←→↑↓10pixel単位のウインドウのリサイズ
Win+Alt+←→↑↓Windows7のエアロスナップ風

■ 参考リンク

2011-08-08

keyhacでクリップボード内のJavaScriptコードを整形/圧縮(JSBeautifier/SlimIt)

JavaScriptをさわってる人ならご存知の人も結構いそうなno title。JavaScriptコードを見やすく整形してくれるWebサービスです。

僕はブックマークレットを作る際など便利でよく使っているのですが、サイトを見てるとコマンドラインバージョンということでPythonコードでも公開されていました。探したらSlimItという圧縮する方も発見。

keyhac*1でクリップボード内のテキストに対して直接実行できたら便利だろうなと書いたのが ↓ のコード。

Win+Jで整形、Win+Sift+Jで高圧縮、Win+Ctrl+Jで低圧縮、というキー配置にしています。

from keyhac import *

## 関数を返す関数にするデコレータ
def ret_func(func):
    import functools

    @functools.wraps(func)
    def _ret_func(*args, **kw):
        @functools.wraps(func)
        def __ret_func():
            return func(*args, **kw)
        return __ret_func
    return _ret_func

## JobQueue/JobItem でサブスレッド処理にするデコレータ
def job_queue(func):
    import functools

    @functools.wraps(func)
    def _job_queue(*args, **kw):

        num_items = JobQueue.defaultQueue().numItems()
        if num_items:   # 処理待ちアイテムがある場合は、その数を表示
            print u"JobQueue.defaultQueue().numItems() :", num_items

        def __job_queue_1(job_item):
            return func(*args, **kw)

        def __job_queue_2(job_item):
            # print "job_queue : ", func.__name__, args, kw
            pass

        job_item = JobItem(__job_queue_1, __job_queue_2)
        JobQueue.defaultQueue().enqueue(job_item)

    return _job_queue

## JavaScriptコードを整形
# JSBeautifier : http://jsbeautifier.org/
@job_queue
def jsbeautifier():
    import jsbeautifier
    clipboard_text = getClipboardText()
    if clipboard_text:
        js_options = jsbeautifier.default_options()

        js_options.jslint_happy = True
        new_code = jsbeautifier.beautify(clipboard_text, js_options)

        setClipboardText(new_code)
        print
        print "/*  jsbeautifier  */"
        print "// before --------------------"
        print clipboard_text
        print "// after --------------------"
        print new_code

## JavaScriptコードを圧縮
# SlimIt : http://slimit.org/
# PLY : http://www.dabeaz.com/ply/
@ret_func
@job_queue
def jsminify(mangle=False):
    import slimit
    clipboard_text = getClipboardText()
    if clipboard_text:
        new_code = slimit.minify(clipboard_text, mangle)

        setClipboardText(new_code)
        print
        print "/*  SlimIt (mangle=%s)  */" % mangle
        print "// before --------------------", len(clipboard_text), "byte"
        print clipboard_text
        print "// after --------------------", len(new_code), "byte"
        print new_code

def configure(keymap):

    keymap_global = keymap.defineWindowKeymap()

    # JavaScriptコードを整形
    keymap_global["W-j"] = jsbeautifier

    # JavaScriptコードを圧縮(高圧縮)
    keymap_global["W-S-j"] = jsminify(mangle=True)

    # JavaScriptコードを圧縮(低圧縮)
    keymap_global["W-C-j"] = jsminify(mangle=False)

■ ライブラリのインストール

・JSBeautifier

https://github.com/einars/js-beautify/raw/master/python/jsbeautifier.py

↑ のファイルをkeyhacのextensionフォルダへコピーする。

・SlimItとPLY(コード解析)

https://github.com/rspivak/slimit

圧縮ファイルをダウンロードし、圧縮ファイルのsrcフォルダ下にslimitをフォルダごとkeyhacのextensionフォルダへコピーする。

http://www.dabeaz.com/ply/

圧縮ファイルをダウンロードし、圧縮ファイルのplyをフォルダごとkeyhacのextensionフォルダへコピーする。

■ さいごに

ブックマークレットをよく書くという方は特に便利だと思うので、よかったら使ってみてください。

■ 参考リンク

*1:Autohotkeyみたいなフリーソフトで、Pythonコードで色んなソフトのショートカットキーを設定できる

2011-07-31

keyhacのPythonスクリプトにも便利なデコレータ

引き続き、keyhacネタ。keyhacPythonでスクリプト(設定ファイル)を記述するのでもちろんデコレータも使えます。デコレータの説明はdecorator.htmlが分かりやすかったので、こちらを見ていただくとして早速コードの紹介。

■ 処理時間計測

上記のサイトで紹介されていたのをちょこっといじったのが、最初に紹介する処理時間計測をするデコレータ

デコレータ
from keyhac import *

## 処理時間計測のデコレータ
def profile(func):
    import functools

    @functools.wraps(func)
    def _profile(*args, **kw):
        import time
        timer = time.clock
        t0 = timer()
        ret = func(*args, **kw)
        print '%s: %.3f [ms] elapsed' % (func.__name__, 1000 * (timer() - t0))
        return ret
    return _profile

↑ をconfig.pyの先頭で定義しておく。そして、以下のように測りたい関数の直前に@profileを挿入する。

・使用サンプル
def configure(keymap):

    ## メモ帳を起動
    @profile
    def exe_notepad():
        shellExecute( None, None, "notepad.exe", u"", u"" )

    keymap_global = keymap.defineWindowKeymap()
    keymap_global["W-n"] = exe_notepad

するとこの関数が実行されたとき(=Win+Nが押されたとき)にkeyhacのコンソール画面に以下のような情報が出力されるという仕掛け。

exe_notepad: 352.611 [ms] elapsed

新しく関数を追加するときなどに、こうした方が処理は速くなるかな?ってときにお手軽に時間計測できます。機能は地味だけどとても便利。

■ サブスレッド処理

つぎはサブスレッド化ってやつを簡単にするデコレータ。処理に時間がかかる関数は、サブスレッド処理で実行することが推奨されているようです。

デコレータ
from keyhac import *

## JobQueue/JobItem でサブスレッド処理にするデコレータ
def job_queue(func):
    import functools

    @functools.wraps(func)
    def _job_queue(*args, **kw):

        num_items = JobQueue.defaultQueue().numItems()
        if num_items:   # 処理待ちアイテムがある場合は、その数を表示
            print u"JobQueue.defaultQueue().numItems() :", num_items

        def __job_queue_1(job_item):
            return func(*args, **kw)

        def __job_queue_2(job_item):
            # print "job_queue : ", func.__name__, args, kw
            pass

        job_item = JobItem(__job_queue_1, __job_queue_2)
        JobQueue.defaultQueue().enqueue(job_item)

    return _job_queue
・使用サンプル

メモ帳起動関数をサブスレッド処理化。(関数定義の前に@job_queueを付ける)

    @job_queue
    def exe_notepad():
        shellExecute( None, None, "notepad.exe", u"", u"" )

もし ↑ をデコレータ使わずに書いたら ↓ になる。

    def exe_notepad():
        def _exe_notepad_1(job_item):
            shellExecute( None, None, "notepad.exe", u"", u"" )

        def _exe_notepad_2(job_item):
            pass

        job_item = JobItem(_exe_notepad_1, _exe_notepad_2)
        JobQueue.defaultQueue().enqueue(job_item)

1つ2つなら良いが、毎回この処理を書くのが面倒になってきて、デコレータを使うようにしたらすごく楽チンになった。

■ キー定義

3つ目はキー定義。このキーが押されたらこの関数の実行するというのを関数定義と一緒に行う。

デコレータ
from keyhac import *

## キー定義用デコレータ
def def_key(window_keymap, key):
    def _def_key(func):
        window_keymap[key] = func
    return _def_key
・使用サンプル

このデコレータは、@def_key(ウィンドウキーマップ, キー表記文字列) という風に使う。

def configure(keymap):

    keymap_global = keymap.defineWindowKeymap()

    ## メモ帳を起動
    @def_key(keymap_global, "W-n")  # Win + N
    def exe_notepad():
        shellExecute( None, None, "notepad.exe", u"", u"" )

これは以下のコードと同等になる。

def configure(keymap):

    ## メモ帳を起動
    def exe_notepad():
        shellExecute( None, None, "notepad.exe", u"", u"" )

    keymap_global = keymap.defineWindowKeymap()
    keymap_global["W-n"] = exe_notepad  # Win + N

このデコレータではコード記述量は減らないが、関数定義とキーへの紐付けを同時にできるのでコードがシンプルで分かりやすくなる。

■ 関数を返す

4つ目は関数を返す関数にするデコレータ。引数をともなう関数をキー割り当てするときには、関数を返す関数にする必要がある。それが面倒になって作ったのがこのデコレータ

デコレータ
from keyhac import *

## 関数を返す関数にするデコレータ
def ret_func(func):
    import functools

    @functools.wraps(func)
    def _ret_func(*args, **kw):
        @functools.wraps(func)
        def __ret_func():
            return func(*args, **kw)
        return __ret_func
    return _ret_func
・使用サンプル

関数定義の前に@ret_funcを挿入する。

def configure(keymap):

    # 引数のパスを起動する
    @ret_func
    def exe_sub(path):
        shellExecute( None, None, path, u"", u"" )

    keymap_global = keymap.defineWindowKeymap()
    keymap_global["W-n"] = exe_sub("notepad.exe")

これは以下のコードと同等になる。

def configure(keymap):

    # 引数のパスを起動する(デコレータ無し版)
    def exe_sub(path):
        def _exe_sub():
            shellExecute( None, None, path, u"", u"" )
        return _exe_sub

    keymap_global = keymap.defineWindowKeymap()
    keymap_global["W-n"] = exe_sub("notepad.exe")

■ さいごに

とりあえずこんなところで。デコレータおもしろい。というかPythonおもしろいです。また何かあったら追加しときます。

追記(2011.08.02)

■ キー定義(別パターン)

3つ目のキー定義の亜種。このサイトの@addtoを使ってwindow_keymapを呼び出し可能にしてしまう。上で書いたやつよりシンプルでいいかもしれない。

デコレータ
from keyhac import *

# http://wiki.python.org/moin/PythonDecoratorLibrary#Easy_adding_methods_to_a_class_instance
def addto(instance):
    def decorator(f):
        import types
        f = types.MethodType(f, instance, instance.__class__)
        setattr(instance, f.func_name, f)
        return f
    return decorator

def configure(keymap):

    def define_window_keymap(*args, **kw):
        window_keymap = keymap.defineWindowKeymap(*args, **kw)
        print args, kw

        @addto(window_keymap)
        def __call__(self, key):
            def def_key(func):
                self[key] = func
            return def_key

        return window_keymap
・使用サンプル
    # keymap.defineWindowKeymap の代わりに define_window_keymap を使う
    keymap_global = define_window_keymap()

    # 通常どおりのキー設定も可能
    keymap_global["S-C-w"] = "LWin"

    # デコレータにもなる
    @keymap_global("W-n")
    def exe_notepad():
        shellExecute( None, None, "notepad.exe", u"", u"" )

■ 参考リンク

2009 | 06 | 07 | 08 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 10 |
2011 | 01 | 02 | 05 | 06 | 07 | 08 | 09 |
2012 | 05 | 06 |
日記の検索

ads
最近のコメント
etc
あわせて読みたいブログパーツ